具有移动迭代器的独特算法

6
可以使用 std::unique 和通过 std::make_move_iterator 函数创建的迭代器吗?我尝试了以下代码,并且成功了。
#include <iostream>
#include <ostream>
#include <vector>
#include <algorithm>
#include <limits>
#include <iterator>

#include <cstdlib>

struct A
{

    A() : i(std::numeric_limits< double >::quiet_NaN()) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
    A(double ii) : i(ii) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
    A(A const & a) : i(a.i) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
    A(A && a) : i(std::move(a.i)) { std::cout << __PRETTY_FUNCTION__ << "\n"; a.i = std::numeric_limits< double >::quiet_NaN(); }
    A & operator = (A const & a) { std::cout << __PRETTY_FUNCTION__ << "\n"; i = a.i; return *this; }
    A & operator = (A && a) { std::cout << __PRETTY_FUNCTION__ << "\n"; i = std::move(a.i); a.i = std::numeric_limits< double >::quiet_NaN(); return *this; }
    bool operator < (A const & a) const { std::cout << __PRETTY_FUNCTION__ << "\n"; return (i < a.i); }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
    bool operator == (A const & a) const { std::cout << __PRETTY_FUNCTION__ << "\n"; return (i == a.i); }
#pragma clang diagnostic pop

    friend
    std::ostream &
    operator << (std::ostream & o, A const & a)
    {
        return o << a.i;
    }

private :

    double i;

};

int
main()
{
    std::vector< A > v{1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 4.0, 4.0, 5.0, 6.0, 6.0, 7.0};
    std::cout << "v constructed\n\n\n\n";
    std::sort(v.begin(), v.end());
    auto const end = std::unique(std::make_move_iterator(v.begin()), std::make_move_iterator(v.end())).base();
    std::copy(v.begin(), end, std::ostream_iterator< A >(std::cout, " "));
    std::cout << std::endl;
    return EXIT_SUCCESS;
}

但也许这是与实现相关的成功呢?

另外,还有来自<numeric><algorithm>的其他算法呢?


std::unique 无论如何都移动,所以我看不出你从中获得了什么好处。 - T.C.
@T.C. 你的意思是什么?在“可能的实现”部分,std::unique 的复制赋值在此处 - Tomilov Anatoliy
1
这不是一个好的实现。真正的库会移动。 - T.C.
@T.C. 他们是否是必须移动的? - Tomilov Anatoliy
它必须支持所有的MoveAssignable类型,因此对于库实现者来说,不移动实际上需要更多的工作。 - T.C.
1个回答

5
该程序保证符合标准。
std::unique需要前向迭代器。展示移动迭代器满足该要求的最简单方法是检查move_iteratoriterator_category typedef:
typedef typename iterator_traits<Iterator>::iterator_category iterator_category;

正如你所看到的,基础迭代器类型的迭代器类别被直接调整。实际上,移动迭代器的行为几乎相当于它们的基础迭代器,[move.iterators]/1:

类模板move_iterator是一个迭代器适配器,其行为与基础迭代器相同,除了其间接操作符将基础迭代器的间接操作符返回的值隐式转换为右值引用。

没有其他值得注意的要求:显然vector<>::iterator是一个输入迭代器(按照[move.iter.requirements]规定)。唯一相关的要求是由unique本身强制执行的:

*first的类型应满足MoveAssignable的要求(表22)。

......这很简单地得到满足。
请注意,使用移动迭代器不应比使用普通迭代器带来任何优势。在内部,重复元素被移动分配(因此需要满足MoveAssignable的要求),因此从operator*返回右值引用是多余的。

不,内部std::unique必须直接使用移动赋值。使用swap也需要MoveConstructible - T.C.
@T.C. 看起来是正确的。那么元素实际上被交换了吗,还是在返回的新结尾之后的元素处于移动状态? - Columbo
@T.C.,libstdc++似乎执行*++__dest = _GLIBCXX_MOVE(*__first),这表明后者是真的。 - Columbo
“remove”有一个注释,说明剩余部分具有有效但未指定的状态。“unique”没有,但仍应该是这种情况。 - T.C.
@Orient 需求是 MoveAssignable - Columbo
“unique” 也需要一个前向迭代器,但这个要求显然也得到了满足。 - T.C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接