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>
?
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 theMoveAssignable
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With