简体   繁体   English

boost :: transform_iterator和std :: iter_swap

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

I am trying to generalize a function I have which used to take two iterators to a vector of a specific data-structure, and re-arrange the elements in a certain way using std::iter_swap (like std::sort does). 我正在尝试概括一个函数,该函数曾经使用两个迭代器转换为特定数据结构的向量,然后使用std::iter_swap以某种方式重新排列元素(就像std::sort一样)。

Since this function only actually needs a subset of the data, and I will need to use it in other contexts in the future, I thought about removing the dependency on the data structure, and use boost::transform_iterator at the point of call to handle the transformation. 由于此函数实际上仅需要数据的一个子集,以后我将需要在其他上下文中使用它,因此我考虑了删除对数据结构的依赖,并在调用处使用boost::transform_iterator进行处理转型。

Unfortunately, it seems that boost::transform_iterator is not happy with this change. 不幸的是,似乎boost::transform_iterator对这一改变并不满意。 I can imagine why: std::iter_swap is usually implemented as std::swap(*lhs, *rhs) , and dereferencing the transform_iterator does not yield the original element to swap in the correct way. 我可以想象为什么: std::iter_swap通常实现为std::swap(*lhs, *rhs) ,并且取消引用transform_iterator不会产生原始元素,无法以正确的方式进行交换。

I was wondering if there was a way to handle this case. 我想知道是否有办法处理这种情况。 I am open to use boost::range or the experimental std::ranges ts if it needed. 我愿意使用boost::range或实验性std::ranges ts(如果需要)。

This question is probably similar to this one , but even there the solution ends up modifying the subset of data the algorithm needs, rather than the outside structure. 这个问题可能与类似,但是即使解决方案最终还是修改了算法所需的数据子集,而不是外部结构。

Here is an MWE: 这是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;
}

Accessing the underlying iterator 访问底层的迭代器

You could access the base() property of transform_iterator (inherited publicly from iterator_adaptor ) to implement your custom transform_iter_swap , for swapping the underlying data of the wrapped iterator. 您可以访问transform_iteratorbase()属性(从iterator_adaptor公开继承)以实现您的自定义transform_iter_swap ,以交换包装的迭代器的基础数据。

Eg: 例如:

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

After which your example (omitting the std::vector part) runs as expected: 之后,您的示例(省略std::vector部分)按预期运行:

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

If you want a general function template to cover both the boost (adaptor) iterators as well as typical iterators, you could eg use if constexpr (C++17) based on whether the iterators public typedef iterator_category derives from boost::iterators::no_traversal_tag or not: 如果您希望通用功能模板同时覆盖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);
    }
}

The problem comes from the unary predicate you've passed. 问题来自您通过的一元谓词。 Notice, that since you allow the return type to be deduced, the return type is deduced to be an int, a copy is returned, and the compilation fails when you try to swap two unmodifiable ints. 请注意,由于您允许推导返回类型,因此将推导类型推导为int,将返回一个副本,并且当您尝试交换两个不可修改的int时,编译将失败。 However, if you were to specify the return type to be int&, like so: 但是,如果将返回类型指定为int&,则如下所示:

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

It will compile, and reverse the elements. 它将编译并反转元素。 Tested on gcc 8.1.0 and clang 6.0.0. 在gcc 8.1.0和clang 6.0.0上测试。

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

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