Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

unique algorithm with move iterators

Is it permittable to use std::unique with iterators created by means of std::make_move_iterator function? I tried the following, and got success:

#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;
}

But maybe is it implementation-dependent success?

And what regarding other algorithms from <numeric> and <algorithm>?

like image 460
Tomilov Anatoliy Avatar asked Jan 28 '15 17:01

Tomilov Anatoliy


1 Answers

The program is guaranteed to work by the standard.
std::unique requires forward iterators. The easiest way to show up that move iterators satisfy that requirement is to inspect the iterator_category typedef of move_iterator:

typedef typename iterator_traits<Iterator>::iterator_category iterator_category;

As you can see, the iterator category of the underlying iterator type is directly adapted. In fact, the behavior of move iterators is almost equivalent to their underlying ones, [move.iterators]/1:

Class template move_iterator is an iterator adaptor with the same behavior as the underlying iterator except that its indirection operator implicitly converts the value returned by the underlying iterator’s indirection operator to an rvalue reference.

There are no other noteworthy requirements: Clearly vector<>::iterator is an input iterator (as required by [move.iter.requirements]). The only relevant requirement imposed by unique itself is

The type of *first shall satisfy the MoveAssignable requirements (Table 22).

... which is straightforwardly met.

Note that using move iterators should not bring any advantage over normal ones. Internally the duplicate elements are move-assigned (hence the MoveAssignable requirement), so returning an rvalue reference from operator* is superfluous.

like image 60
Columbo Avatar answered Nov 09 '22 02:11

Columbo