【发布时间】:2018-03-04 15:11:21
【问题描述】:
我想创建一个自定义迭代器包装器,例如enumerate:给定一对T 类型的迭代器,它将返回一个可迭代类型std::pair<const int, T&> 的迭代器,其中该对的第一个元素将采用值 0、1、2,依此类推。
我无法确定我的迭代器的 value_type 和 reference 应该是什么。我想支持两种行为:
首先,引用底层序列的值:
for (auto& kv: enumerate(my_vec)) {
kv.second = kv.first;
}
(有点像std::iota);
二、复制值:
std::vector<int> a{10, 20, 30};
auto copy = *enumerate(a).begin();
a[0] = 15;
std::cout << copy.first << " " << copy.second; // 0 10
我很困惑Iterator::operator*() 的返回类型应该是什么。如果是std::pair<const int, T&>,那么在第二个示例中的值将不会被复制。如果是std::pair<const int, T>,那么在第一个示例中,不可能引用基础值。这种迭代器的value_type、reference和pointer typedefs应该怎么做?
这是我实现它的尝试。它支持引用但不复制。
template<typename T>
struct Iterator {
using TT = typename std::iterator_traits<T>::value_type;
using value_type = std::pair<const int, TT>;
using reference = std::pair<const int&, typename std::iterator_traits<T>::reference>;
using pointer = value_type*;
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
std::pair<int, T> it;
Iterator(T iterator) : it(0, iterator) {}
bool operator==(const Iterator& other) const { return it.second == other.it.second; }
bool operator!=(const Iterator& other) const { return it.second != other.it.second; }
reference operator*() { return { it.first, *it.second }; }
Iterator& operator++() { ++it.first; ++it.second; return *this; }
};
附:我刚刚检查过,boost::adaptors::index 遇到了同样的问题,并且没有复制值。
【问题讨论】:
-
您或许能够做到这一点,但这并不容易。
operator*必须返回一个代理类(不是直接的pair)。该类的second成员又将是一个代理类,存储对基础元素的引用及其原始值的副本。然后它将实现operator T()(返回原始值)以及operator=(将赋值转发给引用)。我质疑结果是否值得这么麻烦。 -
不知道你为什么会有疑问。可迭代类型
std::pair<const int, T&>应使用std::pair<const int, T&>作为值和std::pair<const int, T&> &作为参考。在第二个示例中,值将按预期复制。 -
@VTT 如果我的引用类型是
std::pair<const int, T&>&,那么我应该将它存储在某个地方,不是吗?看来在这种情况下我会返回一个不好的临时参考。 -
@IgorTandetnik 看起来可行,但我确实应该考虑是否真的需要它。谢谢。
-
你绝对应该把它存储在某个地方。另请注意,当用户只想检查值时,由于取消引用迭代器而返回新的(复制的值)可能会导致相当大的开销,并且会阻止迭代器使用不可复制的类型。