c++ - 实现 Boost 范围适配器 reversed_if

标签 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);
}

std::vector<Foo> v(range.begin(), range.end());
if (reverse) boost::range::reverse(v);
for (auto const & x : v) do_stuff(x);

其中包含代码重复(第一个)或效率低下(第二个)。

我一直在想一个假设的 Boost 范围适配器,它可以有条件地反转一个范围,这样我就可以写

using boost::adaptors::reversed_if;
for (auto const & x : range | reversed_if(reverse)) do_stuff(x);

我可以自己实现它(从 here 开始)但我不确定如何进行。为了支持运行时条件,恐怕我必须在每次迭代时检查一个 bool 值以确定迭代方向或使用虚拟性来分派(dispatch)迭代代码。这就是 Boost 范围适配器中未提供的原因吗?

任何替代解决方案?

最佳答案

如果你想避免在每次增量时检查运行时要走哪条路,你必须将运行时值转换为循环结构之外的编译时值。

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

执行此操作的简单方法是为主体编写一个 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);
}

我们已经在循环之外完成了运行时分派(dispatch),创建了两个不同的循环,其中包含关于它们如何循环的静态类型信息。

我们可以制作这样的适配器:

magic_switch
  ( reverse )
  ( range, range|reversed )
  (
    [&](auto&& range){
      for (auto const& x : decltype(range)(range)) {
        do_stuff(x);
      }
    }
  );

其中 magic_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) );
    };
  };
}

是一个实现的草图(它几乎肯定包含构建错误)。

困难的部分是“类型流”(创造一个术语)不会按照您通常希望的方式进行。所以我基本上不得不使用 continuation-passing-style。

请注意,许多编译器对包含整个 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) );
  };
}

然后将表格替换为:

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

在那些编译器上。

关于c++ - 实现 Boost 范围适配器 reversed_if,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46364955/

相关文章:

javascript - 针对 NodeList 调用 Array.prototype.slice 的目的是什么?

c++ - 将字符串数组输入到函数 C++

c++ - 使用匿名管道替代 PeekNamePipe() -- WIN32

c++ - Boost 图形库 - 未找到 adjacent_vertices 函数

php - 当我将所有图像设置为 col-6 时,为什么我的图像会位于彼此下方?

c++ - 如何在lambda运算中获取当前迭代器

c++ - 使用 boost 序列化到磁盘后无法加载回数据

c++ - 通过 const & 写入类成员

c++ - boost 测试是否支持宽字符串?

C++ 删除字符串中多余的空格或制表符