2011-02-01 7 views
22

Estoy escribiendo una aplicación para las dos ventanas de linux &, y me di cuenta de que la compilación de GCC produce muchas llamadas inútiles al constructor de copias.Contenedores de biblioteca estándar que producen muchas copias en valores r en GCC

He aquí un ejemplo de código para producir este comportamiento:

struct A 
{ 
    A()    { std::cout << "default" << std::endl; } 
    A(A&& rvalue)  { std::cout << "move" << std::endl; } 
    A(const A& lvalue) { std::cout << "copy" << std::endl; } 
    A& operator =(A a) { std::cout << "assign" << std::endl; return *this; } 
}; 

BOOST_AUTO_TEST_CASE(test_copy_semantics) 
{ 
    std::vector<A> vec_a(3); 
} 

Esta prueba sólo crea un vector de 3 elementos. Espero 3 llamadas al constructor predeterminadas y 0 copias, ya que no hay A lvalues.

En Visual C++ 2010, la salida es:

default 
move 
default 
move 
default 
move 

En GCC 4.4.0 (MinGW), (-O2 -std = C++ 0x), la salida es:

default 
copy 
copy 
copy 

¿Qué está pasando y cómo lo soluciono? Las copias son caras para la clase real, la construcción predeterminada y los movimientos son baratos.

+1

¿Ha mirado el encabezado ''? ¿Qué está haciendo el constructor? El comportamiento de GCC es consistente con la especificación C++ 03, donde un objeto se construye por defecto y luego se copia N veces. Es posible que su versión de la biblioteca estándar no admita el nuevo constructor agregado a C++ 0x, que construye por defecto N elementos. –

+1

Como el ejemplo usa las características del lenguaje C++ 0x, supongo que esta es una pregunta sobre la especificación C++ 0x, no sobre la especificación C++ 03. –

+1

Mismo resultado con GCC 4.5 – tstenner

Respuesta

18

Ambas implementaciones (Visual C++ 2010 y GCC 4.4.0) tienen un error. La salida correcta es:

default 
default 
default 

Esto se especifica en 23.3.5.1 [vector.cons]/4:

requiere: T será DefaultConstructible.

La implementación no puede suponer que A es MoveConstructible ni CopyConstructible.

+5

+1, buena captura en el lado VS de ella. –

+0

Tienes razón, mi siguiente pregunta sería por qué VC se está moviendo construyendo en absoluto. ¿Hay algo que se pueda hacer? ¿Alguna idea de si las implementaciones de stlport o ea son más conformes? – Inverse

+0

Este std :: lib: http://libcxx.llvm.org/ obtiene la respuesta correcta, pero no se ha portado a Windows. Hay partes interesadas en portarlo a Windows y que sin duda podrían usar ayuda. –

1

Prueba esto a continuación:

std::vector<A> vec_a; 
vec_a.reserve(3); 
for (size_t i = 0; i < 3; ++i) 
    vec_a.push_back(A()); 

Lo que estamos tratando de hacer es forzar el proceso de inicialización a utilizar constructo + mover para cada valor en lugar de construcción y luego copiar/copia/copia. Estas son solo filosofías diferentes; los autores de las bibliotecas no podrían saber cuál será el mejor para un tipo dado.

+4

Esto es más que filosofías diferentes. El borrador de C++ 0x especifica el comportamiento correcto. –

+1

+1, el código propuesto es, de hecho, una solución. –

6

Parece que el problema es que la versión de g ++ que tiene no tiene una biblioteca C++ 0x totalmente compatible. En particular, en C++ 03, el constructor tamaño de std :: vector tiene la siguiente firma:

// C++ 03 
explicit vector(size_type n, const T& value = T(), 
const Allocator& = Allocator()); 

Con esa función de la firma y su llamada, se crea un temporal, a continuación, obligados por la referencia constante y copias de ella se crean para cada uno de los elementos.

mientras que en C++ 0x hay diferentes constructores:

// C++0x 
explicit vector(size_type n); 
vector(size_type n, const T& value, const Allocator& = Allocator()); 

En este caso, la llamada se corresponde con la primera firma, y ​​los elementos por defecto debe ser construida con la colocación de nuevo sobre el recipiente (como @ Howard Hinnant señala correctamente en su answer que el compilador no debe llamar al constructor de movimientos en absoluto).

Usted puede probar y comprobar si las versiones más recientes de g ++ haber una actualización de la biblioteca estándar, o se puede evitar el problema agregando manualmente los elementos:

std::vector<A> v; 
v.reserve(3);  // avoid multiple relocations 
while (v.size() < 3) v.push_back(A()); 
1

Puede añadir caso especial (barato) a copie el algoritmo de ctor al copiar el objeto construido por defecto a "este" objeto. Es solo una solución, sin embargo, el comportamiento es bastante extraño. Ambos compiladores (bibliotecas) crean un objeto temporal en la pila, luego gcc lo copia a los objetivos 3 veces; msvc recrea el objeto temporal 3 veces (!) (en la pila también) y se mueve 3 veces a los objetivos. No entiendo por qué no crean objetos directamente en su lugar.

0

Creo que las 3 variantes no infringen el borrador de C++ 0x. Requiere siguiente: 1. Constructos de un vector con elementos de valor inicializado n 2. T será DefaultConstructible 3. lineal en n

Todas las 3 variantes satisfacen 1, como defecto + copia, por defecto + movimiento son equivalentes a predeterminado Las 3 variantes satisfacen 3 Las 3 variantes satisfacen 2: funcionan para los tipos DefaultConstructible. Algoritmo específico se puede usar para tipos movibles. Es una práctica general en STL utilizar diferentes versiones de algoritmos para tipos con diferentes capacidades.

Cuestiones relacionadas