C++ 如果需要,实施增压范围适配器

C++ 如果需要,实施增压范围适配器,c++,boost,foreach,boost-range,boost-adaptors,C++,Boost,Foreach,Boost Range,Boost Adaptors,我经常在代码中遇到这样的情况,即我希望根据运行时的条件以直接或相反的顺序迭代某个范围。这通常会产生如下代码 if (reverse) { using boost::adaptors::reversed; for (auto const & x : range | reversed) do_stuff(x); } else { for (auto const & x : range) do_stuff(x); } 或 我可以自己实现它(从开始),但我不确定

我经常在代码中遇到这样的情况,即我希望根据运行时的条件以直接或相反的顺序迭代某个范围。这通常会产生如下代码

if (reverse) {
    using boost::adaptors::reversed;
    for (auto const & x : range | reversed) do_stuff(x);
} else {
    for (auto const & x : range) do_stuff(x);
}

我可以自己实现它(从开始),但我不确定如何继续。为了支持运行时条件,恐怕我必须在每次迭代时检查一个布尔值来确定迭代方向,或者使用虚拟性来调度迭代代码。这就是为什么增压范围适配器中不提供此功能的原因吗


是否有其他解决方案?

如果要避免在每次增量时进行运行时检查,则必须将运行时值转换为循环结构之外的编译时值

在这种情况下,我们希望循环的范围发生变化,而实体不发生变化

实现这一点的简单方法是为主体编写lambda,然后使用开关选择要选择的循环

auto do_stuff = [&](auto&& elem){ /* code */ };
if (reverse) {
  using boost::adaptors::reversed;
  for (auto const & x : range | reversed) do_stuff(x);
} else {
  for (auto const & x : range) do_stuff(x);
}
我们在循环之外完成了运行时调度,创建了两个不同的循环,其中包含关于它们如何循环的静态类型信息

我们可以制作如下适配器:

magic_switch
  ( reverse )
  ( range, range|reversed )
  (
    [&](auto&& range){
      for (auto const& x : decltype(range)(range)) {
        do_stuff(x);
      }
    }
  );
其中,
magic\u switch
将索引(
std::size\t
)作为其第一个参数。它返回一个lambda,它接受一个参数列表。它返回一个lambda,它接受一个lambda并将第二个列表中的参数传递给它,该参数由第一个参数在该列表中的索引确定

inline auto magic_switch( std::size_t I ) {
  return [I](auto&&...options) {
    return [I, &](auto&& f)->decltype(auto) {
      using fptr = void(*)(void const volatile* op, decltype(f));
      static const fptr table[] = {
        +[](void const volatile* op_in, decltype(f) f) {
          auto* option = static_cast<std::decay_t<decltype(options)>*>(op_in);
          decltype(f)(f)( decltype(options)(*option) );
        }...
      };
      const volatile void* ptrs[] = {std::addressof(options)...};
      if (I >= sizeof...(options)) I = sizeof...(options)-1;
      if (I == -1) return;
      table[I]( ptrs[I], decltype(f)(f) );
    };
  };
}
内联自动魔术开关(标准::大小\u t I){
返回[I](自动&&…选项){
返回[I,&](自动)&&f)->decltype(自动){
使用fptr=void(*)(void const volatile*op,decltype(f));
静态常数fptr表[]={
+[](void const volatile*op_in,decltype(f)f){
自动*选项=静态投切(投切);
decltype(f)(f)(decltype(options)(*option));
}...
};
常量volatile void*ptrs[]={std::addressof(选项)…};
如果(I>=sizeof…(选项))I=sizeof…(选项)-1;
如果(I==-1)返回;
表[I](ptrs[I],decltype(f)(f));
};
};
}
是实现的草图(几乎肯定包含构建错误)

困难的部分是“类型流”(创造一个术语)没有按照你通常希望的方式进行。所以我不得不基本上采用连续传球的方式

请注意,许多编译器对包含整个lambda的包扩展不满意。可以编写返回函数指针的帮助函数:

template<class F>
using f_ptr = void(*)(const volatile void*, F&&);

template<class Option, class F>
f_ptr<F> get_f_ptr() {
  return +[](void const volatile* op_in, F&& f) {
    auto* option = static_cast<std::decay_t<Option>*>(op_in);
    std::forward<F>(f)( std::forward<Option>(*option) );
  };
}
模板
使用f_ptr=void(*)(常数volatile void*,f&&);
模板
f_ptr get_f_ptr(){
return+[](void const volatile*op_in,F&&F){
自动*选项=静态投切(投切);
标准::转发(f)(标准::转发(*选项));
};
}
然后将表格替换为:

      static const fptr table[] = {
        get_fptr<decltype(options), decltype(f)>()...
      };
static const fptr table[]{
获取\u fptr()。。。
};

在那些编译器上。

哇!非常感谢。我明白你的想法,但我会尽力理解你的魔术开关的实现细节。
decltype(range)(range)
的意义是什么?这是一个演员阵容吗?为什么需要它?@ChristopherFuzier提供了一个
自动&
参考,可以完美地转发。将其读取为“范围与声明的类型相同”。它相当于
std::forward(range)
,但只要
range
auto&
T&
(转发参考)类型,它的长度就只有std::forward(range)的一半。如果
range
是一种值类型(
auto
T
),则它不起作用。@Christopher还注意到
magic_开关可以基于
variant
工作。
template<class F>
using f_ptr = void(*)(const volatile void*, F&&);

template<class Option, class F>
f_ptr<F> get_f_ptr() {
  return +[](void const volatile* op_in, F&& f) {
    auto* option = static_cast<std::decay_t<Option>*>(op_in);
    std::forward<F>(f)( std::forward<Option>(*option) );
  };
}
      static const fptr table[] = {
        get_fptr<decltype(options), decltype(f)>()...
      };