C++ 具有复杂值类型的迭代器:与值类型和引用混淆
我想创建一个自定义迭代器包装,例如,C++ 具有复杂值类型的迭代器:与值类型和引用混淆,c++,iterator,C++,Iterator,我想创建一个自定义迭代器包装,例如,enumerate:给定类型T上的一对迭代器,它将返回类型std::pair上的一个iterable,其中该对的第一个元素将取值0、1、2,依此类推 我在计算迭代器的value\u类型和引用时遇到了一个问题。我想支持两种行为: 首先,引用基础序列的值: for (auto& kv: enumerate(my_vec)) { kv.second = kv.first; } (有点像std::iota) 第二,复制价值: std::vector&
enumerate
:给定类型T
上的一对迭代器,它将返回类型std::pair
上的一个iterable,其中该对的第一个元素将取值0、1、2,依此类推
我在计算迭代器的value\u类型
和引用
时遇到了一个问题。我想支持两种行为:
首先,引用基础序列的值:
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
另外,我刚刚检查过,boost::Adapters::index也有同样的问题,并且没有复制值。这个问题类似于
std::vector
,您希望提供一个代理,它的作用类似于引用,但也支持值语义
但不同的是,涉及的类型没有限制,涉及到两个参考,各种毛羽都会弹出。下面是一个部分实现,它说明了您遇到的一些问题
#include<iterator>
#include<functional>
template<typename F, typename S, bool defined = true>
struct sfinae_difference_type {};
template<typename F, typename S>
struct sfinae_difference_type<F, S,
std::is_same_v<typename std::iterator_traits<F>::difference_type,
typename std::iterator_traits<S>::difference_type>>
{
using difference_type = typename std::iterator_traits<F>::difference_type;
};
template<typename F, typename S>
class pair_iterator : sfinae_difference_type<F, S>
{
using Fvalue_type = typename std::iterator_traits<F>::value_type;
using Svalue_type = typename std::iterator_traits<S>::value_type;
using Freference = typename std::iterator_traits<F>::reference;
using Sreference = typename std::iterator_traits<S>::reference;
F f;
S s;
public:
using value_type = std::pair<Fvalue_type, Svalue_type>;
struct reference
{
Freference first;
Sreference second;
reference() = delete;
reference(const reference& other) : first{other.first}, second{other.second} {}
reference& operator=(const reference& rhs)
{
first = rhs.first;
second = rhs.second;
return *this;
}
operator value_type() { return {f, s}; }
private:
reference(Freference f, Sreference s) : first{f}, second{s} {}
friend pair_iterator;
};
struct pointer
{
// similar to reference
};
pair_iterator() = default;
pair_iterator(const pair_iterator&) = default;
pair_iterator(F f, S s) : f{f}, s{s} {}
pair_iterator& operator++() { ++f; ++s; return *this; }
reference operator*() { return {*f, *s}; }
pointer operator->() { return {f.operator->(), s.operator->()}; }
bool operator==(const pair_iterator& other)
{
return f == other.f && s == other.s;
}
};
#包括
#包括
模板
结构sfinae_difference_type{};
模板
结构sfinae\u差异\u类型
{
使用difference\u type=typename std::iterator\u traits::difference\u type;
};
模板
类对迭代器:sfinae\u difference\u类型
{
使用Fvalue\u type=typename std::iterator\u traits::value\u type;
使用Svalue\u type=typename std::iterator\u traits::value\u type;
使用freeference=typename std::iterator\u traits::reference;
使用Sreference=typename std::iterator\u traits::reference;
F;
S S;
公众:
使用value_type=std::pair;
结构引用
{
频率优先;
参考第二;
reference()=删除;
引用(const reference&other):第一个{other.first},第二个{other.second}{
引用和运算符=(常量引用和rhs)
{
first=rhs.first;
秒=rhs.second;
归还*这个;
}
运算符值_type(){return{f,s};}
私人:
参考(f参考,s参考):第一个{f},第二个{s}
朋友对迭代器;
};
结构指针
{
//类似于参考
};
pair_iterator()=默认值;
pair_iterator(const pair_iterator&)=默认值;
成对迭代器(F,S):F{F},S{S}
成对迭代器和运算符+++(){++f;++s;返回*this;}
引用运算符*(){return{*f,*s};}
指针运算符->(){return{f.operator->(),s.operator->()};}
布尔运算符==(常量对迭代器和其他)
{
返回f==other.f&&s==other.s;
}
};
然后你把它当作
#include<vector>
#include<list>
#include<iostream>
int main()
{
std::vector v{1, 2, 3, 4, 5};
std::list l{6, 7, 8, 9, 10};
pair_iterator begin{v.begin(), l.begin()}, end{v.end(), l.end()};
for(; begin != end; ++begin)
std::cout << begin->first << ' ' << begin->second << '\n';
}
#包括
#包括
#包括
int main()
{
std::向量v{1,2,3,4,5};
std::列表l{6,7,8,9,10};
成对迭代器begin{v.begin(),l.begin()},end{v.end(),l.end()};
for(;begin!=end;++begin)
std::cout首先您可能能够实现这一点,但这并不容易。操作符*
必须返回一个代理类(而不是直接的对
)。该类的第二个成员反过来将是一个代理类,存储对底层元素的引用及其原始值的副本。然后它将实现运算符T()
(返回原始值)以及运算符=
(将赋值转发到引用)。我怀疑结果是否值得这么麻烦。我不知道你为什么有任何疑问。Iterable over typestd::pair
应使用std::pair
作为值,并使用std::pair&
作为引用。在第二个示例中,值将按预期进行复制。@VTT如果我的引用类型是std::pair&
,那么我应该这样做将其存储在某个地方,不是吗?在这种情况下,我似乎会返回对临时值的引用,但这并不好。@IgorTandetnik看起来可行,但我确实应该考虑是否真的需要它。谢谢。您肯定应该将其存储在某个地方。还请注意,返回新的(复制的值)当用户只想检查值时,解引用迭代器可能会导致相当大的开销,并会阻止迭代器使用不可复制的类型。谢谢,这看起来很有趣。关于指针的另一个问题:据我所知,在这个实现中,我们希望将引用
实例存储在指针中
类并返回运算符->()
中的引用是否正确?如果正确,为什么要将地址(*f)
而不是*f
传递到指针的构造函数中?@IvanSmirnov我犯了一个错误,应该是f.operator->()
相反,以避免获取临时代理引用的地址。指针应可为空,并支持算术运算(如果有意义的话),使用引用时这两个都是不可能的
#include<vector>
#include<list>
#include<iostream>
int main()
{
std::vector v{1, 2, 3, 4, 5};
std::list l{6, 7, 8, 9, 10};
pair_iterator begin{v.begin(), l.begin()}, end{v.end(), l.end()};
for(; begin != end; ++begin)
std::cout << begin->first << ' ' << begin->second << '\n';
}