条件提升::range::join

Conditional boost::range::join

提问人:Denis Sheremet 提问时间:5/1/2021 最后编辑:seheDenis Sheremet 更新时间:5/1/2021 访问量:56

问:

我想写一个这样的函数:

template<class IterableType>
void CheckAndProcessIterables(IterableType& a, IterableType& b, IterableType& c) {
  IteratorRangeType range{}; // empty range
  if (Check(a)) {
    range = boost::range::join(range, a);
  }
  if (Check(b)) {
    range = boost::range::join(range, b);
  }
  if (Check(c)) {
    range = boost::range::join(range, c);
  }
  Process(range);
}

可能吗?我应该使用哪种类型而不是? 据我了解,返回类型取决于它的参数。是否有一些包装类可以分配任何类型的范围,只要其基础值类型相同?IteratorRangeTypeboost::range::join

C++ 加速 提升范围

评论


答:

1赞 sehe 5/1/2021 #1

您可以使用类型擦除的迭代器范围,Boost 的格式为 。any_range

请注意这些性能成本,这很快就会变得非常明显。我会重新考虑这种方法,除非你非常确定这不在任何热门路径上,可读性比性能更受关注。

在 CoCompiler Explorer 上直播

#include <boost/range/join.hpp>
#include <boost/range/any_range.hpp>

// for demo only:
#include <boost/range/algorithm/for_each.hpp>
#include <boost/lambda/lambda.hpp>
#include <fmt/ranges.h>

template<class Range>
bool Check(Range const& r) {
    bool odd_len = boost::size(r) % 2;
    fmt::print("Check {}, odd_len? {}\n", r, odd_len);
    return odd_len;
}

template<class Range>
void Process(Range const& r) {
    fmt::print("Processing {}\n", r);
    using namespace boost::lambda;
    for_each(r, _1 *= _1);
}

template<class IterableType>
void CheckAndProcessIterables(IterableType& a, IterableType& b, IterableType& c) {
    using V = typename boost::range_value<IterableType>::type;
    using ErasedRange= boost::any_range<V, boost::forward_traversal_tag>;

    ErasedRange range{}; // empty range
    if (Check(a)) {
        range = boost::range::join(range, a);
    }
    if (Check(b)) {
        range = boost::range::join(range, b);
    }
    if (Check(c)) {
        range = boost::range::join(range, c);
    }
    Process(range);
}

int main() {
    std::vector a{1, 2, 3}, b{4, 5}, c{6, 7, 8};
    CheckAndProcessIterables(a, b, c);

    fmt::print("After process: a:{} b:{} c:{}\n", a, b, c);
}

指纹

Check {1, 2, 3}, odd_len? true
Check {4, 5}, odd_len? false
Check {6, 7, 8}, odd_len? true
Processing {1, 2, 3, 6, 7, 8}
After process: a:{1, 4, 9} b:{4, 5} c:{36, 49, 64}

评论

0赞 sehe 5/1/2021
只是为了灵感,这可能是一个替代方案,仍然非常通用,没有任何类型擦除:godbolt.org/z/7xsxqPoqW
0赞 sehe 5/1/2021
和往常一样(...),使用std::ranges完成这项工作似乎比它应该的要困难得多。我花了半个小时才体面地工作:godbolt.org/z/soobjYrrM 从好的方面来说,它保持了单一范围的“逻辑形状”,并且不应该产生巨大的开销(如)。Processany_range
0赞 sehe 5/1/2021
我可能应该将这些版本作为单独的答案发布,以便从诸如此类的边缘问题中获得更多的代表:)
0赞 Denis Sheremet 5/2/2021
谢谢,这正是我想要的。当然,在间接成本接近生产环境之前,我会彻底考虑它,但我知道这个东西以我想象的方式存在,我感到很自满。