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 type
std::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';
}