我写了一个花哨的“zip 迭代器”,它已经完成了许多角色(可以用于 for_each、复制循环、容器迭代器范围构造函数等......)。
在处理所涉及的对/元组的所有模板代码下,归结为迭代器的解引用运算符返回一个元组/一对引用,而不是对元组/对的引用。
我希望我的迭代器与 std::sort
一起工作,所以我需要能够做 swap(*iter1, *iter2)
并在迭代的原始容器中切换基础值。
代码和一个小demo可以看这里(有点看不懂):http://coliru.stacked-crooked.com/a/4fe23b4458d2e692
尽管 libstdc++ 的排序使用 std::iter_swap
其中调用 swap
,例如libc++ 没有,它只是调用 swap
直接,所以我想要一个涉及 swap
的解决方案作为定制点。
我已经尝试过(并且非常接近工作)而不是返回 std::pair
/std::tuple
来自 operator*
正如我现在所做的,返回的是一个简单的包装器类型。目的是让包装器表现得好像它是 std::pair
/std::tuple
,并允许我写一个 swap
为它发挥作用。
它看起来像这样:
template<typename... ValueTypes>
struct TupleWrapper : public PairOrTuple_t<ValueTypes...>
{
using PairOrTuple_t<ValueTypes...>::operator=;
template<typename... TupleValueTypes>
operator PairOrTuple_t<TupleValueTypes...>() const
{
return static_cast<PairOrTuple_t<ValueTypes...>>(*this);
}
};
template<std::size_t Index, typename... ValueTypes>
decltype(auto) get(TupleWrapper<ValueTypes...>& tupleWrapper)
{
return std::get<Index>(tupleWrapper);
}
template<std::size_t Index, typename... ValueTypes>
decltype(auto) get(TupleWrapper<ValueTypes...>&& tupleWrapper)
{
return std::get<Index>(std::forward<TupleWrapper<ValueTypes...>>(tupleWrapper));
}
template<typename... ValueTypes,
std::size_t... Indices>
void swap(TupleWrapper<ValueTypes...> left,
TupleWrapper<ValueTypes...> right,
const std::index_sequence<Indices...>&)
{
(std::swap(std::get<Indices>(left), std::get<Indices>(right)), ...);
}
template<typename... ValueTypes>
void swap(TupleWrapper<ValueTypes...> left,
TupleWrapper<ValueTypes...> right)
{
swap(left, right, std::make_index_sequence<sizeof...(ValueTypes)>());
}
namespace std
{
template<typename... ValueTypes>
class tuple_size<utility::implementation::TupleWrapper<ValueTypes...>> : public tuple_size<utility::implementation::PairOrTuple_t<ValueTypes...>> {};
template<std::size_t Index, typename... ValueTypes>
class tuple_element<Index, utility::implementation::TupleWrapper<ValueTypes...>> : public tuple_element<Index, utility::implementation::PairOrTuple_t<ValueTypes...>> {};
}
完整代码在这里:http://coliru.stacked-crooked.com/a/951cd639d95af130 .在
operator*
中返回此包装器似乎可以编译(至少在 GCC 上)但会产生垃圾。在 Clang 的 libc++ 上,
std::tie
无法编译。两个问题:
我知道它有很多代码,但是我不能把它写得更短,因为所有交换元组包装器的小例子对我来说都很好。
最佳答案
第一个问题
问题之一是ZipIterator
类不满足 RandomAccessIterator
的要求.
std::sort
需要 RandomAccessIterator
s 作为其参数 RandomAccessIterator
s 必须是 BidirectionalIterator
s BidirectionalIterator
s 必须是 ForwardIterator
s ForwardIterator
s 的条件是 ::reference
必须是 value_type&
/const value_type&
:The type
std::iterator_traits<It>::reference
must be exactly
T&
ifIt
satisfiesOutputIterator
(It
is mutable)const T&
otherwise (It
is constant)(where
T
is the type denoted bystd::iterator_traits<It>::value_type
)
其中
ZipIterator
目前没有实现。它适用于
std::for_each
和类似的函数,只需要迭代器满足 InputIterator
的要求/ OutputIterator
.The
reference
type for an input iterator that is not also aLegacyForwardIterator
does not have to be a reference type: dereferencing an input iterator may return a proxy object orvalue_type
itself by value (as in the case ofstd::istreambuf_iterator
).
tl;博士:
ZipIterator
可以用作 InputIterator
/OutputIterator
,但不是 ForwardIterator
, 其中 std::sort
需要。第二个问题
如 @T.C.在 their comment 中指出
std::sort
允许将值移出容器,然后再将它们移回。The type of dereferenced
RandomIt
must meet the requirements ofMoveAssignable
andMoveConstructible
.
其中
ZipIterator
当前无法处理(它从不复制/移动引用的对象),所以这样的事情不能按预期工作:std::vector<std::string> vector_of_strings{"one", "two", "three", "four"};
std::vector<int> vector_of_ints{1, 2, 3, 4};
auto first = zipBegin(vector_of_strings, vector_of_ints);
auto second = first + 1;
// swap two values via a temporary
auto temp = std::move(*first);
*first = std::move(*second);
*second = std::move(temp);
// Result:
/*
two, 2
two, 2
three, 3
four, 4
*/
( test on Godbolt )结果
不幸的是,不可能创建一个迭代器来动态生成元素并且可以用作
ForwardIterator
使用当前标准(例如 this question )InputIterator
s/OutputIterator
s(或以不同方式处理您的 ZipIterator
)例如一个简单的冒泡排序:( Godbolt )
template<class It>
void bubble_sort(It begin, It end) {
using std::swap;
int n = std::distance(begin, end);
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-i-1; j++) {
if (*(begin+j) > *(begin+j+1))
swap(*(begin+j), *(begin+j+1));
}
}
}
ZipIterator
类满足RandomAccessIterator
.不幸的是,如果不将元组放入像数组这样的动态分配结构中(您可能试图避免这种结构),我想不出一种可能的方法
关于c++ - 与 get、tie 和其他元组操作一起使用的元组包装器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63137322/