boost :: transform_iterator和std :: iter_swap

时间:2018-08-25 22:21:18

标签: c++ boost iterator c++17

我正在尝试概括一个函数,该函数曾经将两个迭代器带到特定数据结构的向量,然后使用std::iter_swap以某种方式重新排列元素(例如{{1} })。

由于此函数实际上仅需要数据的一个子集,以后我将需要在其他上下文中使用它,因此我考虑删除对数据结构的依赖,并在此时使用std::sort调用以处理转换。

很不幸,boost::transform_iterator似乎对此更改不满意。我可以想象为什么:boost::transform_iterator通常实现为std::iter_swap,而取消引用std::swap(*lhs, *rhs)并不能产生原始元素,不能以正确的方式进行交换。

我想知道是否有办法处理这种情况。我愿意使用transform_iterator或实验性boost::range ts(如果需要)。

这个问题可能与this one类似,但是即使解决方案最终还是修改了算法所需的数据子集,而不是外部结构。

这是MWE:

std::ranges

2 个答案:

答案 0 :(得分:2)

访问底层的迭代器

您可以访问transform_iterator的{​​{3}}属性(从iterator_adaptor公开继承)来实现自定义transform_iter_swap,以交换包装的迭代器的基础数据。 / p>

例如:

template<class IteratorAdaptor>
void transform_iter_swap(IteratorAdaptor a, IteratorAdaptor b)
{
   std::swap(*a.base(), *b.base());
}

template <typename It>
void my_invert(It begin, It end) {
    while (begin < end) {
        transform_iter_swap(begin++, --end);
    }
}

之后,您的示例(省略std::vector部分)将按预期运行:

my_invert(begin, end); // OK
my_print(begin, end);  // 3 5 7 9

如果您希望通用功能模板同时涵盖boost(适配器)迭代器和典型迭代器,则可以例如根据迭代器公共if constexpr typedef是否从iterator_category派生而使用boost::iterators::no_traversal_tag(C ++ 17):

// expand includes with
#include <boost/iterator/iterator_categories.hpp>

template <class It>
void iter_swap(It a, It b) {
  if constexpr(std::is_base_of<
        boost::iterators::no_traversal_tag,
        typename It::iterator_category>::value) {
      std::swap(*a.base(), *b.base());
    }
  else {
    std::swap(*a, *b);
  }
}

template <typename It>
void my_invert(It begin, It end) {
    while (begin < end) {
        iter_swap(begin++, --end);
    }
}

答案 1 :(得分:1)

问题来自您通过的一元谓词。请注意,由于您允许推导返回类型,因此将推导类型推导为int,将返回一个副本,并且当您尝试交换两个不可修改的int时,编译将失败。但是,如果将返回类型指定为int&,则像这样:

 auto unwrap = [](A & a)->int& { return a.x; }; // explicitly demand to return ref

它将编译并反转元素。在gcc 8.1.0和clang 6.0.0上进行了测试。