2012-03-08 9 views
40

Estoy tratando de inicializar un std::vector<std::unique_ptr<std::string>> de una manera que es equivalente a un ejemplo de Bjarne Stroustrup's C++11 FAQ:Inicialización contenedor de unique_ptrs de lista de inicialización falla con GCC 4.7

using namespace std; 
vector<unique_ptr<string>> vs { new string{"Doug"}, new string{"Adams"} }; // fails 
unique_ptr<string> ps { new string{"42"} }; // OK 

no puedo ver ninguna razón por la cual esta sintaxis fallara . ¿Hay algún problema con esta forma de inicializar el contenedor?
El mensaje de error del compilador es enorme; el segmento relevante que encuentro es el siguiente:

/usr/lib/gcc-snapshot/lib/gcc/i686-linux-gnu/4.7.0/../../../../include/c++/4.7.0 /bits/stl_construct.h:77:7: error: no matching function for call to 'std::unique_ptr<std::basic_string<char> >::unique_ptr(std::basic_string<char>&)'

¿Cuál es la manera de solucionar este error?

+3

Está recogiendo el iterador de entrada ctor – PlasmaHH

+0

Muy similar a http://stackoverflow.com/a/9504162/841108 –

+0

@PlasmaHH En mi código real tenía muchas entradas en la lista de inicializadores, así que no creo este es el problema. – juanchopanza

Respuesta

47

unique_ptr 's constructor es explicit. Entonces no puedes crear uno implícitamente desde new string{"foo"}. Tiene que ser algo así como unique_ptr<string>{ new string{"foo"} }.

Lo que nos lleva a esta

vector<unique_ptr<string>> vs { 
    unique_ptr<string>{ new string{"Doug"} }, 
    unique_ptr<string>{ new string{"Adams"} } 
}; 

Sin embargo, pueden tener fugas si uno de los constructores falla. Es más seguro utilizar make_unique:

vector<unique_ptr<string>> vs { 
    make_unique<string>("Doug"), 
    make_unique<string>("Adams") 
}; 

Pero ... initializer_list s siempre realizan copias, y unique_ptr s no son copiables. Esto es algo realmente molesto acerca de las listas de inicializadores. Puede hack around it, o puede recurrir a la inicialización con llamadas al emplace_back.

Si en realidad está administrando string s con punteros inteligentes y no es solo por el ejemplo, entonces puede hacerlo aún mejor: simplemente haga un vector<string>. El std::string ya maneja los recursos que usa.

+0

@Xeo, de hecho, http://stackoverflow.com/questions/6804216/how-to-initialize-a-container-of-noncopyable-with-initializer-list – juanchopanza

+0

+1 para los enlaces! Y no, yo soy en realidad no le preocupa hacer un vector de unique_ptrs a la cadena. Es solo el ejemplo de las preguntas frecuentes. – juanchopanza

+5

El primer ejemplo no tendrá fugas. ',' en la lista de inicializadores es un punto de secuencia (en la nueva terminología, algo más engorrosa) , en '{a, b}', 'a' se secuencia antes de' b'). Véase C++ 11 §8.5.4/4. Sin embargo, es una buena idea usar 'make_unique'. –

2

Después de "fijar" su ejemplo:

#include <vector> 
#include <memory> 
#include <string> 

int main() 
{ 
    std::vector<std::unique_ptr<std::string>> vs = { { new std::string{"Doug"} }, { new std::string{"Adams"} } }; // fails 
    std::unique_ptr<std::string> ps { new std::string{"42"} }; // OK 
} 

que tiene mensaje de error muy claro:

error: converting to 'std::unique_ptr<std::basic_string<char> >' from initializer list would use explicit constructor 'std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = std::basic_string<char>, _Dp = std::default_delete<std::basic_string<char> >, std::unique_ptr<_Tp, _Dp>::pointer = std::basic_string<char>*]' 

Este error indica que no es posible utilizar el contructor explícita unique_ptr 's.

+6

Aaaand ... ese mensaje de error no nos dice nada. * ¿Por qué * no es posible usar el ctor explícito? – Xeo

+0

@Xeo - ¿Qué parte de ese trabajo * explícitamente * llama al ctor? Puede haber una llamada implícita allí, pero eso está prohibido. – Flexo

+3

La semántica de un unique_ptr es que no se puede copiar, por eso se elimina el constructor. ¿Pero no debería 'std :: vector' ser consciente de los movimientos y así ser capaz de pasar de un temporal al vector? – hirschhornsalz

Cuestiones relacionadas