2012-08-05 17 views
14

El constructor std :: shared_ptr no se está comportando como lo esperaba:std :: shared_ptr y inicializador

#include <iostream> 
#include <vector> 

void func(std::vector<std::string> strings) 
{ 
    for (auto const& string : strings) 
    { 
     std::cout << string << '\n'; 
    } 
} 

struct Func 
{ 
    Func(std::vector<std::string> strings) 
    { 
     for (auto& string : strings) 
     { 
      std::cout << string << '\n'; 
     } 
    } 
}; 

int main(int argc, const char * argv[]) 
{ 

    func({"foo", "bar", "baz"}); 
    Func({"foo", "bar", "baz"}); 
    //auto ptr = std::make_shared<Func>({"foo", "bar", "baz"}); // won't compile. 
    //auto ptr = std::make_shared<Func>{"foo", "bar", "baz"}; // nor this. 
    return 0; 
} 

¿Estoy haciendo algo mal o es el compilador? El compilador es:

$ ruido metálico ++ --version de Apple sonido metálico versión 4.0 (etiquetas/de Apple/ruido metálico-421.0.57) (basado en LLVM 3.1svn)

edición: shared_ptr en lugar de make_shared.

Aquí está el error:

make -k 
clang++ -std=c++11 -stdlib=libc++ main.cc -o main 
main.cc:28:18: error: no matching function for call to 'make_shared' 
     auto ptr = std::make_shared<Func>({"foo", "bar", "baz"}); 
       ^~~~~~~~~~~~~~~~~~~~~~ 
/usr/bin/../lib/c++/v1/memory:4621:1: note: candidate function not viable: 
    requires 0 arguments, but 1 was provided 
make_shared(_Args&& ...__args) 
^ 
1 error generated. 

Respuesta

21

Prueba esto:

auto ptr = std::make_shared<Func>(std::initializer_list<std::string>{"foo", "bar", "baz"}); 

Clang no está dispuesto a deducir el tipo de {"foo", "bar", "baz"}. Actualmente no estoy seguro de si esa es la forma en que se supone que funciona el lenguaje, o si estamos viendo un error del compilador.

+5

Lo último que escuché es que el reenvío perfecto no es perfecto en absoluto cuando se trata de listas de inicializadores. – Puppy

+2

{"foo", "bar", "baz"} no es una expresión y como tal no tiene ningún tipo (excepto cuando se usa con auto) .. Aunque sería bueno – Cubbi

3

Es necesario utilizar make_shared si desea crear un nuevo objeto, construido a partir de estos argumentos, apuntado por un shared_ptr. shared_ptr<T> es como un puntero a T - debe construirse con un puntero a T, no un T.

Editar: El reenvío perfecto de hecho no es del todo perfecto cuando se trata de listas de inicializadores (que es la mamada). No es un error en el compilador. Deberá crear un valor r de tipo Func manualmente.

+0

Disculpa, el problema estaba en make_shared. Lo cambié a shared_ptr mientras futzing. – dpj

4

El constructor de shared_ptr<T> toma como puntero un puntero de tipo T*, asumiendo que apunta a un recurso dinámicamente asignado (o al menos algo que puede ser liberado por el eliminador). Por otro lado, make_shared hace la construcción por usted y toma los argumentos del constructor directamente.

Así que, o dicen esto:

std::shared_ptr<Foo> p(new Foo('a', true, Blue)); 

O, mucho mejor y más eficiente:

auto p = std::make_shared<Foo>('a', true, Blue); 

Esta última forma se encarga de la asignación y la construcción para usted, y en el proceso crea una implementación más eficiente.

Por supuesto, también podría decir make_shared<Foo>(Foo('a', true, Blue)), pero eso crearía una copia innecesaria (que puede eludirse) y, lo que es más importante, creará redundancia innecesaria. [Edición] Para inicializar su vector, este puede ser el mejor método:

auto p = std::make_shared<Func>(std::vector<std::string>({"a", "b", "c"})); 

El punto importante es, sin embargo, que make_sharedrealiza la asignación dinámica para usted, mientras que el constructor-ptr compartida hace no, y en su lugar toma la propiedad.

+0

lo siento. ¡Ustedes son tan rápidos!El problema fue con make_shared. – dpj

+0

"* pero eso solo crearía una copia innecesaria *" Un movimiento innecesario. – ildjarn

+0

@ildjarn: Bueno, eso depende de todo, ¿no? De todos modos, "mover" es solo una copia optimizada :-) –

Cuestiones relacionadas