[英]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_iterator
的base()
属性(从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.