2012-08-05 8 views
14

Konstruktor std :: shared_ptr nie zachowuje się zgodnie z oczekiwaniami:std :: shared_ptr i initializer listy

#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; 
} 

Czy robię coś źle lub jest kompilator? Kompilator jest:

$ dzyń ++ --version Jabłko dzyń wersja 4.0 (tags/Apple/dzyń-421.0.57) (oparty na LLVM 3.1svn)

edit: shared_ptr zamiast make_shared.

Oto błąd:

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. 

Odpowiedz

21

Spróbuj tego:

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

Clang nie jest skłonny wywnioskować typ {"foo", "bar", "baz"}. Obecnie nie jestem pewien, czy to jest sposób, w jaki powinien działać język, czy też patrzymy na błąd kompilatora.

+5

Ostatnio słyszałem, wiadomo, że perfekcyjne przekazywanie nie jest w ogóle doskonałe, jeśli chodzi o listy inicjalizatorów. – Puppy

+2

{"foo", "bar", "baz"} nie jest wyrażeniem i jako takie nie ma typu (z wyjątkiem użycia z auto). Chociaż byłoby dobrze – Cubbi

3

Trzeba użyć make_shared jeśli chcesz utworzyć nowy obiekt, zbudowany z tych argumentów, wskazywanego przez shared_ptr. shared_ptr<T> jest jak wskaźnik do T - musi być skonstruowany ze wskaźnikiem na T, a nie na T.

Edycja: Perfect forwarding w rzeczywistości nie jest doskonały, gdy zaangażowane są listy inicjalizatorów (co jest do dupy). To nie jest błąd w kompilatorze. Będziesz musiał ręcznie utworzyć wartość rytuału typu Func.

+0

Niestety, problemem był make_shared. Zmieniłem go na shared_ptr podczas futzing. – dpj

4

Konstruktor obiektu shared_ptr<T> przyjmuje jako argument wskaźnik o typie T*, który zakłada, że ​​wskazuje na dynamicznie przydzielony zasób (lub co najmniej coś, co może zostać zwolniony przez element usuwający). Z drugiej strony, make_shared tworzy dla ciebie konstrukcję i bezpośrednio pobiera argumenty konstruktora.

Więc albo powiedzieć:

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

Albo, dużo lepiej i bardziej efektywnie:

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

Ta ostatnia forma dba o przydziale i budowy dla Ciebie, a proces tworzy bardziej wydajna realizacja.

Można oczywiście również powiedzieć: make_shared<Foo>(Foo('a', true, Blue)), ale to po prostu tworzy niepotrzebną kopię (która może zostać usunięta), a co ważniejsze, tworzy zbędną nadmiarowość. [Edit] Do inicjowania swój wektor, to może być najlepszą metodą:

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

Ważne jest jednak, że make_sharedwykonuje dynamiczną alokację dla Ciebie, podczas gdy konstruktor shared-ptr robi nie, a zamiast tego przejmuje na własność.

+0

Przepraszam. Jesteście tacy szybcy!Problem polegał na make_shared. – dpj

+0

"* ale to by tworzyło niepotrzebną kopię *" Niepotrzebny ruch. – ildjarn

+0

@ildjarn: Cóż, to wszystko zależy, prawda? W każdym razie "move" to tylko zoptymalizowana kopia :-) –