2015-01-28 17 views
6

È possibile utilizzare std::unique con gli iteratori creati mediante la funzione std::make_move_iterator? Ho provato the following, ed ha ottenuto il successo:algoritmo unico con iteratori di movimento

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

Ma forse è che il successo dipende dall'implementazione?

E per quanto riguarda altri algoritmi da <numeric> e <algorithm>?

+0

'std :: unique' si sposta comunque, quindi non vedo cosa si ottiene da esso. –

+0

@ T.C. Cosa intendi? 'std :: unique' copy-assigning [qui] (http://en.cppreference.com/w/cpp/algorithm/unique) nella sezione" Possibile implementazione ". – Orient

+1

Questa non è una buona implementazione. Librerie reali [sposta] (http://coliru.stacked-crooked.com/a/198b772ffc2f41ba). –

risposta

5

Il programma è garantito per funzionare secondo lo standard.
std::unique richiede iteratori in avanti. Il modo più semplice per mostrare che iteratori mossa soddisfa tale requisito è quello di ispezionare il iterator_category typedef di move_iterator:

typedef typename iterator_traits<Iterator>::iterator_category iterator_category; 

Come si può vedere, la categoria iteratore del tipo iteratore sottostante è direttamente adattato. In realtà, il comportamento di iteratori mossa è quasi equivalente al loro quelli sottostanti, [move.iterators]/1:

modello Classe move_iterator è un adattatore iteratore con lo stesso comportamento come l'iteratore sottostante, tranne che il suo riferimento indiretto L'operatore converte implicitamente il valore restituito dall'operatore di riferimento indiretto dello iteratore su un riferimento di rvalue.

ci sono altri requisiti notevoli: Chiaramente vector<>::iterator è un iteratore di input (come richiesto dalla [move.iter.requirements]). L'unico requisito relativo imposto dal unique sé è

il tipo di *first deve soddisfare le esigenze MoveAssignable (Tabella 22).

... che viene soddisfatta direttamente.

Si noti che l'uso di iteratori di movimento non dovrebbe portare alcun vantaggio rispetto a quelli normali. Internamente gli elementi duplicati vengono assegnati allo spostamento (da qui il requisito MoveAssignable), pertanto la restituzione di un riferimento rvalue da operator* è superflua.

+0

No, internamente 'std :: unique' deve utilizzare direttamente l'assegnazione del movimento. Usare 'swap' richiederebbe anche' MoveConstructible'. –

+0

@ T.C. Sembra essere corretto Gli elementi sono effettivamente scambiati, o sono quelli dopo la nuova fine restituita in uno stato spostato da? – Columbo

+0

@ T.C. libstdC++ sembra fare '* ++ __ dest = _GLIBCXX_MOVE (* __ first)', che indica che quest'ultimo è vero. – Columbo

Problemi correlati