繁体   English   中英

boost :: transform_iterator和std :: iter_swap

[英]boost::transform_iterator and std::iter_swap

我正在尝试概括一个函数,该函数曾经使用两个迭代器转换为特定数据结构的向量,然后使用std::iter_swap以某种方式重新排列元素(就像std::sort一样)。

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

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

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

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

这是MWE:

#include <boost/iterator/transform_iterator.hpp>
#include <vector>
#include <algorithm>
#include <iostream>

struct A {
    int x;
    int y;
};

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

template <typename It>
void my_print(It begin, It end) {
    for (; begin != end; ++begin)
        std::cout << (*begin) << ' ';
    std::cout << '\n';
}

int main() {
    std::vector<int> x{7,6,5,4,3,2};

    my_invert(std::begin(x), std::end(x));
    my_print(std::begin(x), std::end(x));

    auto unwrap = +[](const A & a) { return a.x; };

    std::vector<A> y{{9,8}, {7,6}, {5,4}, {3,2}};

    auto begin = boost::make_transform_iterator(std::begin(y), unwrap);
    auto end = boost::make_transform_iterator(std::end(y), unwrap);

    //my_invert(begin, end); // Does not work.
    my_print(begin, end);

    return 0;
}

访问底层的迭代器

您可以访问transform_iteratorbase()属性(从iterator_adaptor公开继承)以实现您的自定义transform_iter_swap ,以交换包装的迭代器的基础数据。

例如:

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 (C ++ 17),具体取决于迭代器public typedef iterator_category是否源自boost::iterators::no_traversal_tag

// 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);
    }
}

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM