2016-05-10 14 views
8

Quando seguente codice:Posso ordinare il vettore di una tupla di riferimenti?

std::vector<std::tuple<int&>> v; 
int a = 5; v.emplace_back(a); 
int b = 4; v.emplace_back(b); 
int c = 3; v.emplace_back(c); 
int d = 2; v.emplace_back(d); 
int e = 1; v.emplace_back(e); 
std::sort(std::begin(v), std::end(v)); 

è compilato con gcc/libstdC++ vs clang/libC++ binario dà risultati diversi.

Per gcc/libstdc++ un elemento viene copiato su tutti gli altri riferimenti.

5 4 3 2 1 
5 5 5 5 5 

ho pensato che clang/libc++ comporta come previsto ma funziona solo fino a 5 elementi in un vettore (causa v'è un caso speciale per piccoli contenitori).

5 4 3 2 1 
1 2 3 4 5 

Quando si passa più elementi il ​​risultato è simile a gcc.

5 4 3 2 1 0 
3 4 5 5 5 5 

Quindi è valido da utilizzare std::sort per il contenitore di tuple con riferimenti (cioè realizzati con std::tie, l'ordinamento sottoinsieme di struct)? In caso contrario, dovrei aspettarmi qualche avviso?

risposta

6

Quindi è valido da utilizzare std::sort per il contenitore di tuple con riferimenti (cioè realizzati con std::tie, l'ordinamento sottoinsieme di struct)? In caso contrario, dovrei aspettarmi eventuali avvertimenti?

No, e no. Uno dei requisiti di tipo su std::sort() è che:

  • Il tipo di Dereferenced RandomIt devono soddisfare i requisiti di MoveAssignable e MoveConstructible.

dove MoveAssignable richiede nell'espressione t = rv:

Il valore di t è equivalente al valore rv prima della cessione.

Ma std::tuple<int&> non è MoveAssignable perché int& non è MoveAssignable. Se si dispone di sufficiente:

int& ra = a; 
int& rb = b; 

ra = std::move(rb); 

Il valore di ra non è equivalente al valore precedente di rb. ra still fa riferimento a a a, non cambia in riferimento a b - ciò che effettivamente è stato modificato è il valore di a.

Poiché il nostro tipo non soddisfa la condizione di std::sort(), il risultato della chiamata std::sort() è solo un comportamento non definito.


Si noti che è possibile sorta una std::vector<std::tuple<std::reference_wrapper<int>>> però, perché è std::reference_wrapper MoveAssignable.

Nota anche che questo ricorda di non essere in grado di ordinare un contenitore di auto_ptr, per un vecchio Herb Sutter article.

+0

L'uso di 'std :: reference_wrapper ' funziona perché può essere costruito da 'std :: tie' ma richiede un po 'di magia del modello per ottenere l'elenco di tipi T & ... e decorarlo con reference_wrapper. [Entrambi i compilatori] (http://melpon.org/wandbox/permlink/atQyUIr1Bh5g6qgn) concordano sul fatto che 'std :: tuple ' is_move_assignable && is_move_constructible (con i tratti normali) –

+0

@KarolWozniak Il tratto può controllare solo se l'attuale semantica funziona - tu * puoi * spostare assegnare una 'tupla '. Semplicemente non ha il comportamento effettivo che quell'operazione deve avere per essere considerato MoveAssignable. – Barry

+0

@KarolWozniak Per esempio, 'struct X {int val; X & operator = (X &&) {return * this; }; '' is_move_assignable 'è vero, ma' X' non è in realtà MoveAssignable poiché il requisito non è soddisfatto. – Barry

Problemi correlati