Unique algorithm with iterators

Is it possible to use std::unique with iterators created using the std::make_move_iterator ? 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(ai) { std::cout << __PRETTY_FUNCTION__ << "\n"; } A(A && a) : i(std::move(ai)) { std::cout << __PRETTY_FUNCTION__ << "\n"; ai = std::numeric_limits< double >::quiet_NaN(); } A & operator = (A const & a) { std::cout << __PRETTY_FUNCTION__ << "\n"; i = ai; return *this; } A & operator = (A && a) { std::cout << __PRETTY_FUNCTION__ << "\n"; i = std::move(ai); ai = std::numeric_limits< double >::quiet_NaN(); return *this; } bool operator < (A const & a) const { std::cout << __PRETTY_FUNCTION__ << "\n"; return (i < ai); } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfloat-equal" bool operator == (A const & a) const { std::cout << __PRETTY_FUNCTION__ << "\n"; return (i == ai); } #pragma clang diagnostic pop friend std::ostream & operator << (std::ostream & o, A const & a) { return o << ai; } 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 this is implementation-dependent success?

What about other algorithms from <numeric> and <algorithm> ?

+6
source share
1 answer

The program is guaranteed to work according to the standard.
std::unique requires an iterator. The easiest way to show that moving iterators satisfy this requirement is to check the iterator_category typedef move_iterator :

 typedef typename iterator_traits<Iterator>::iterator_category iterator_category; 

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

The class template move_iterator is an iterator adapter with the same behavior as the main iterator, except that its indirectness operator implicitly converts the value returned by the base iterator to the rvalue help system.

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

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

... which directly meets.

Note that using move iterators should not bring any advantages over normal ones. Internally duplicate elements are assigned along the way (hence the MoveAssignable requirement), so returning an rvalue link from operator* is unnecessary.

+5
source

Source: https://habr.com/ru/post/981680/


All Articles