我可以将std :: nth_element与ValueSwappable迭代器一起使用,但不使用MoveConstructible取消引用的值吗?

时间:2016-05-05 18:25:04

标签: c++ algorithm c++11

根据this documentation of std::nth_element,RandomIt必须满足以下约束条件:

  

-RandomIt必须满足ValueSwappable和RandomAccessIterator的要求。

     

- 解除引用的RandomIt类型必须满足MoveAssignable和MoveConstructible的要求。

似乎第二个要求与RandomIt的ValueSwappable是多余的,考虑到std :: nth_element的算法是如何工作的(即它们通常仅在带有交换的解除引用的迭代器上工作(* it1,* it2))。第二个要求保证如果没有定义特定于类型的交换,std :: swap将起作用。

在我的例子中,我想创建一个由两个迭代器组成的迭代器,其行为就好像所有操作都应用于两个迭代器一样。虽然我没有遇到满足ValueSwappable约束的麻烦,但我不知道如何满足MoveConstructible和MoveAssignable要求以及我的目的。

这是一个这样的迭代器的例子:

template <typename ItA, typename ItB>
struct pair_iterator {

    using VA = typename std::iterator_traits<ItA>::value_type;
    using VB = typename std::iterator_traits<ItB>::value_type;

    ItA a;
    ItB b;
    pair_iterator &operator++() { ++a; ++b; return *this; }
    pair_iterator &operator--() { --a; --b; return *this; }
    friend long operator-(const pair_iterator &iterator1, const pair_iterator &iterator2) {
        return iterator1.a-iterator2.a;
    }

    friend pair_iterator operator+(const pair_iterator &it, size_t increment) {
        return pair_iterator{it.a+increment, it.b+increment};
    }
    friend pair_iterator operator-(const pair_iterator &it, size_t increment) {
        return pair_iterator{it.a-increment, it.b-increment};
    }
    bool operator==(const pair_iterator &it) const { return it.a == a && it.b == b; }
    bool operator!=(const pair_iterator &it) const { return it.a != a || it.b != b; }
    bool operator>=(const pair_iterator &it) const { return a >= it.a; }
    bool operator<=(const pair_iterator &it) const { return a <= it.a; }
    bool operator<(const pair_iterator &it) const { return a < it.a; }
    bool operator>(const pair_iterator &it) const { return a > it.a; }

    struct Value {
        VA &a;
        VB &b;
        friend void swap(const Value &l, const Value &r) {
            using std::swap;
            swap(l.a, r.a);
            swap(l.b, r.b);
        }
    };

    Value operator*() const { return Value{*a, *b}; }

    using difference_type = long;
    using value_type = Value;
    using pointer = void;
    using reference = Value;
    using iterator_category = std::random_access_iterator_tag;
};

值类型(值)不可移动(也可以任何方式分配),因为它包含引用。

请注意,使用clang ++的std :: nth_element对这样的迭代器进行测试确实有效,但如果按照我的理解相信上述要求,则C ++库的不同实现可能会破坏我的代码。

我错过了什么吗?

1 个答案:

答案 0 :(得分:0)

以上是与libstdc ++一起使用的上述迭代器的修改版本。

template <typename ItA, typename ItB>
struct pair_iterator {

        using VA = typename std::iterator_traits<ItA>::value_type;
        using VB = typename std::iterator_traits<ItB>::value_type;

        ItA a;
        ItB b;
        pair_iterator &operator++() { ++a; ++b; return *this; }
        pair_iterator &operator--() { --a; --b; return *this; }
        friend long operator-(const pair_iterator &iterator1, const pair_iterator &iterator2) {
                return iterator1.a-iterator2.a;
        }

        friend pair_iterator operator+(const pair_iterator &it, size_t increment) {
                return pair_iterator{it.a+increment, it.b+increment};
        }
        friend pair_iterator operator-(const pair_iterator &it, size_t increment) {
                return pair_iterator{it.a-increment, it.b-increment};
        }
        bool operator==(const pair_iterator &it) const { return it.a == a && it.b == b; }
        bool operator!=(const pair_iterator &it) const { return it.a != a || it.b != b; }
        bool operator>=(const pair_iterator &it) const { return a >= it.a; }
        bool operator<=(const pair_iterator &it) const { return a <= it.a; }
        bool operator<(const pair_iterator &it) const { return a < it.a; }

        struct ValueCopy {
                VA a;
                VB b;
        };
        struct Value {
                VA &a;
                VB &b;
                friend void swap(const Value &l, const Value &r) {
                        using std::swap;
                        swap(l.a, r.a);
                        swap(l.b, r.b);
                }
                Value &operator=(Value&&v) {
                        a = v.a;
                        b = v.b;
                        return *this;
                }
                Value &operator=(const ValueCopy &v) {
                        a = v.a;
                        b = v.b;
                        return *this;
                }
                operator ValueCopy() const { return ValueCopy{a,b}; }
        };


        Value operator*() { return {*a, *b}; }

        using difference_type = long;
        using value_type = ValueCopy;
        using pointer = void;
        using reference = Value;
        using iterator_category = std::random_access_iterator_tag;
};

这里的技巧是定义value_type和reference,以便* it1 = * it2;有效,但值v = * it; * it2 = v; 我仍然不确定这是否会一直有效。

PS:澄清为什么我想要一个&#34;代理迭代器&#34;,这是因为我想对一个向量执行nth_element操作,同时能够构建一个旧索引的向量数据向量。我知道这可以通过在索引向量上使用比较运算符来完成,但经验表明这种方法比同时对数据排序要慢很多,因为如果索引被移动会发生缓存垃圾但不是数据。