2015-06-07 18 views
6

Considerare questo codice:copie spurie in C++ 03 libstdC++ vs C++ 11

#include <iostream> 
#include <string> 
#include <map> 

using namespace std; 

class Foo 
{ 
public: 
    Foo() : _x(0) 
    { 
     cout << "Default" << endl; 
    } 
    Foo(int a) : _x(a) 
    { 
     cout << "Param" << endl; 
    } 

    Foo(Foo const &foo) : 
     _x(foo._x) 
    { 
     cout << "Copy" << endl; 
    } 

    Foo& operator=(Foo const &foo) 
    { 
     cout << "Assignment" << endl; 
     _x = foo._x; 
     return *this; 
    } 

    int get(void) 
    { 
     return _x; 
    } 

private: 
    int _x; 
}; 

int main(int argc, char *argv []) 
{ 
    std::map<int, Foo> foos; 

    Foo a_foo(10); 

    foos[100] = a_foo; 

    return 0; 
} 

compilata in gcc con -std = C++ 11 e si ottiene l'uscita,

Param 
Default 
Assignment 

Rimuovere -STD = C++ 11, poi si arriva,

Param 
Default 
Copy 
Copy 
Assignment 

with c++11

without

libc++ example producing the superior output in c++03 mode

Dove sono le due copie in più provenienti da?

Sono relativi alla chiamata dell'operatore di pedice, non all'assegnazione. (Rimangono se rimuovi il compito). Per me non sembrano essere necessari, nemmeno in un mondo pre-C++ 11, come mostra l'esempio di libC++.

Questo è stato originariamente motivato, cercando in this question

+2

non si ottiene loro in modalità C++ 11 quindi perché non rimuovere tutte le funzioni rvalue-ref dalla questione del tutto? La domanda non è ovviamente influenzata da loro. –

+0

Mi aspetto che questa sia una cosa di QoI all'interno di libstdC++ e suppongo che ci vorrebbe qualche cosa per determinare con precisione cosa sta succedendo. –

+0

@LightnessRacesinOrbit Sono lì perché la domanda non è influenzata da loro, in contraddizione con la risposta precedente. – tahsmith

risposta

9

Questo è LWG 334:

I C++ 03 Mandati di serie i seguenti effetti per operator[] ([lib.map.access] p1):

Resi:(*((insert(make_pair(x, T()))).first)).second.


libstdC++ implementa l'inserimento usata da operator[] (nel caso in cui la chiave non esiste ancora) come segue in C++ 03 modalità:

__i = insert(__i, value_type(__k, mapped_type())); 

__i è il punto di inserimento , viene calcolato come

iterator __i = lower_bound(__k); 

__k è il parametro di operator[].

La creazione del codice provvisorio value_type(__k, mapped_type()) causa la prima copia (da mapped_type() nella coppia value_type). La seconda copia è il risultato di insert, che copia la coppia value_type in un nodo effettivo.

La versione originale del 1997 è: (! Che non esisteva nemmeno allora)

return (*((insert(value_type(k, T()))).first)).second; 

che è quasi alla lettera della norma. L'ultima volta che è stato cambiato in modo significativo è stato nel 1998. Prima di questo, ha usato:

__i = insert(__i, value_type(__k, _Tp())); 

Il messaggio di commit dice che questo è stato quello di

Update per SGI STL 3.11.


Earlier versions of the SGI STL (1995) ha infatti specificare map::operator[] nello stesso modo come il C++ 03 standard:

Per una mappa m e la chiave k, m[k] è semanticamente equivalente a (*((m.insert(make_pair(k, T()))).first)).second.

SGI STL v2.03 (1997) era già passato a usando value_type anziché make_pair. E come suggerisce il log di commit di gcc, l'implementazione di SGI STL è cambiata nuovamente tra v3.0 (anche 1997) e v3.11 (1998) da insert(value_type(.. al modulo ancora presente in libstdC++ usando lower_bound e creando la coppia solo se la chiave non esiste ancora.


Quindi si potrebbe dire che libstdC++ implementa la prima proposta di delibera di LWG 334 (value_type invece di make_pair). Questo non è esattamente quello che è successo, però, guardando alla sua storia. Sta semplicemente seguendo SGI STL. libC++ non è strettamente conforme a C++ 03 a questo riguardo.


La versione C++ 11 di libstdC++ dello stesso operatore utilizza una funzione di posizionamento personalizzata. Specifiche Il C++ 11 di standard di map::operator[] segue la risoluzione proposta di LWG 334:

Effetti: Se non esiste un equivalente chiave per x nella mappa, inserisce value_type(x, T()) nella mappa.

(dove x è il parametro di operator[])

+0

Per quanto ne so, C++ 03 non ha modificato gli effetti di 'map :: operator []' rispetto a C++ 98. – dyp

+0

Grazie, è stato interessante e completo. In C++ 11, la formulazione degli effetti sembra essere ciò che è in LWG 334. – tahsmith

+0

@tahsmith Sì, mi sono fermato un po 'presto lì.Ho incluso la formulazione C++ 11 ora. È stato anche abbastanza interessante scavare nella storia git di gcc! Fortunatamente, mi sono ricordato che c'era un problema di LWG relativo a 'operator []', quindi trovare LWG 334 non era troppo difficile. – dyp

Problemi correlati