Dlaczego std::make_unique
w standardowej bibliotece C ++ 11 nie ma szablonu funkcji? znajduję
std::unique_ptr<SomeUserDefinedType> p(new SomeUserDefinedType(1, 2, 3));
trochę gadatliwy. Czy poniższe rzeczy nie byłyby o wiele ładniejsze?
auto p = std::make_unique<SomeUserDefinedType>(1, 2, 3);
To new
ładnie ukrywa i wspomina o typie tylko raz.
Tak czy inaczej, oto moja próba wdrożenia make_unique
:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
Sporo czasu zajęło mi std::forward
skompilowanie tego pliku , ale nie jestem pewien, czy jest poprawny. Czy to jest Co dokładnie std::forward<Args>(args)...
znaczy? Co robi z tego kompilator?
c++
c++11
variadic-templates
unique-ptr
perfect-forwarding
fredoverflow
źródło
źródło
unique_ptr
bierze drugi parametr szablonu, na który powinieneś jakoś zezwolić - który jest inny niżshared_ptr
.make_unique
za pomocą niestandardowego programu usuwającego, ponieważ oczywiście alokuje on za pomocą zwykłego staregonew
i dlatego należy używać zwykłego staregodelete
:)make_unique
byłby ograniczony donew
alokacji ... no cóż, dobrze jest, jeśli chcesz to napisać, ale rozumiem, dlaczego coś takiego nie jest częścią standardu.make_unique
szablonu, ponieważ konstruktorstd::unique_ptr
jest jawny, a zatem zwracanie sięunique_ptr
z funkcji jest pełne . Wolałbym też użyćauto p = make_unique<foo>(bar, baz)
niżstd::unique_ptr<foo> p(new foo(bar, baz))
.make_unique
kapieC++14
, zobacz isocpp.org/blog/2013/04/trip-report-iso-c-spring-2013-meetingOdpowiedzi:
Herb Sutter, przewodniczący komitetu normalizacyjnego C ++, pisze na swoim blogu :
Podaje także implementację identyczną z tą podaną przez PO.
Edycja:
std::make_unique
teraz jest częścią C ++ 14 .źródło
make_unique
Szablon funkcja sama w sobie nie gwarantuje bezpieczne połączenia wyjątków. Opiera się na konwencji, że osoba dzwoniąca z niej korzysta. Natomiast ścisłe sprawdzanie typów statycznych (które jest główną różnicą między C ++ i C) opiera się na zasadzie wymuszania bezpieczeństwa poprzez typy. I do tegomake_unique
może być po prostu klasą zamiast funkcji. Na przykład zobacz mój artykuł na blogu z maja 2010 r. Jest również powiązany z dyskusją na blogu Herb.make_unique
klasy, myślę, że teraz lepiej jest uczynić ją funkcją, która generujemake_unique_t
, przyczyną tego jest problem z najbardziej perwersyjną analizą :-).make_unique
oferuje silną gwarancję wyjątku. A może miksujesz narzędzie z jego użyciem, w którym to przypadku żadna funkcja nie jest wyjątkowo bezpieczna. Zastanów sięvoid f( int *, int* ){}
, wyraźnie oferujeno throw
gwarancję, ale według twojego rozumowania nie jest ona wyjątkowo bezpieczna, ponieważ może być nadużywana. Co gorsza,void f( int, int ) {}
wyjątek też nie jest bezpieczny !:typedef unique_ptr<int> up; f( *up(new int(5)), *up(new int(10)))
...make_unique
wdrożone jak wyżej, a ty mi punkt artykule Sutter, artykuł, że moje google-fu wskazał mi do państw, któremake_unique
oferuje silny gwarancja wyjątek , który zaprzecza swoje oświadczenie powyżej. Jeśli masz inny artykuł, jestem zainteresowany jego przeczytaniem. Więc moje oryginalne pytanie brzmi: Jakmake_unique
(jak zdefiniowano powyżej) wyjątek nie jest bezpieczny? (Sidenote: tak, myślę, żemake_unique
poprawia to bezpieczeństwo wyjątkowe w miejscach, w których można je zastosować)make_unique
funkcji. Po pierwsze, nie widzę, jak ten jest niebezpieczny i nie widzę, jak dodanie dodatkowego typu uczyniłoby go bezpieczniejszym . Wiem, że jestem zainteresowany zrozumieniem problemów, które może mieć ta implementacja - nie widzę żadnych - oraz tego, jak alternatywna implementacja mogłaby je rozwiązać. Jakie są konwencje , któremake_unique
zależy? W jaki sposób zastosowałbyś sprawdzanie typu w celu wymuszenia bezpieczeństwa? To są dwa pytania, na które chciałbym odpowiedzieć.Fajnie, ale Stephan T. Lavavej (lepiej znany jako STL) ma lepsze rozwiązanie
make_unique
, które działa poprawnie dla wersji tablicowej.Można to zobaczyć na jego filmie Core C ++ 6 .
Zaktualizowana wersja make_unique STL jest teraz dostępna jako N3656 . Ta wersja została przyjęta do wersji roboczej C ++ 14.
źródło
make_unique
powinna iść w nagłówku. Nagłówki nie powinny importować przestrzeni nazw (patrz pozycja # 59 w książce Sutter / Alexandrescu „C ++ Standardy kodowania”). Zmiany w Xeo pomagają uniknąć zachęcania do złych praktyk.std::make_shared
nie jest tylko skrótemstd::shared_ptr<Type> ptr(new Type(...));
. Robi coś, bez czego nie można się obejść.Aby wykonać swoje zadanie,
std::shared_ptr
musi przydzielić blok śledzenia oprócz przechowywania pamięci dla rzeczywistego wskaźnika. Ponieważ jednakstd::make_shared
alokuje rzeczywisty obiekt, możliwe jest, żestd::make_shared
alokuje zarówno obiekt, jak i blok śledzenia w tym samym bloku pamięci.Tak więc podczas gdy
std::shared_ptr<Type> ptr = new Type(...);
byłyby dwa przydziały pamięci (jeden dlanew
, jeden wstd::shared_ptr
bloku śledzenia),std::make_shared<Type>(...)
przydzieliłby jeden blok pamięci.Jest to ważne dla wielu potencjalnych użytkowników
std::shared_ptr
. Jedyne, costd::make_unique
można zrobić, to nieco wygodniej. Nic więcej.źródło
Chociaż nic nie stoi na przeszkodzie, aby napisać własnego pomocnika, uważam, że głównym powodem udostępnienia
make_shared<T>
w bibliotece jest to, że faktycznie tworzy on inny wewnętrzny typ wskaźnika wspólnego niżshared_ptr<T>(new T)
, który jest inaczej przydzielany, i nie ma sposobu, aby to osiągnąć bez dedykowanego pomocnik.Korekta: w rzeczywistości nie jest to prawdą: wywołanie funkcji w celu zawinięciamake_unique
Z drugiej strony twoje opakowanie to zwykły cukier syntaktyczny wokółnew
wyrażenia, więc chociaż może wyglądać przyjemnie dla oka, nie przynosi niczegonew
na stół.new
wyrażenia zapewnia bezpieczeństwo wyjątków, na przykład w przypadku wywołania funkcjivoid f(std::unique_ptr<A> &&, std::unique_ptr<B> &&)
. Posiadanie dwóch nieprzetworzonych względem siebie surowychnew
znaków oznacza, że jeśli jedno nowe wyrażenie zawiedzie z wyjątkiem, drugie może przeciekać zasoby. Jeśli chodzi o to, dlaczego nie ma tegomake_unique
w normie: zostało po prostu zapomniane. (Zdarza się to czasami. Wstd::cbegin
standardzie nie ma również globalnego, chociaż powinien istnieć.)Zauważ też, że
unique_ptr
bierze drugi parametr szablonu, na który powinieneś jakoś zezwolić; różni się to od tegoshared_ptr
, który używa kasowania typu do przechowywania niestandardowych programów usuwających bez włączania ich do typu.źródło
shared_ptr<T>(new T)
używa jednego z nich,make_shared<T>()
używa innego. Pozwalając, że jest to Dobra Rzecz, a wersja dzielona jest w pewnym sensie najlżejszym wspólnym wskaźnikiem, jaki można uzyskać.shared_ptr
przydziela blok pamięci dynamicznej, aby zachować liczbę i akcję „disposer” podczas tworzeniashared_ptr
. Jeśli wskaźnik zostanie przekazany jawnie, musi utworzyć „nowy” blok, jeślimake_shared
go użyjesz , możesz spakować swój obiekt i dane satelitarne w jednym bloku pamięci (jedennew
), co spowoduje szybsze przydzielanie / zwalnianie, mniejszą fragmentację i (zwykle ) lepsze zachowanie pamięci podręcznej.W C ++ 11
...
jest używany (w kodzie szablonu) również do „rozszerzenia pakietu”.Wymagane jest, abyś używał go jako sufiksu wyrażenia zawierającego nierozwiniętą paczkę parametrów, a po prostu zastosuje to wyrażenie do każdego z elementów paczki.
Na przykład, opierając się na twoim przykładzie:
To ostatnie jest niepoprawne.
Ponadto pakiet argumentów nie może być przekazany do funkcji nierozwiniętej. Nie jestem pewien co do zestawu parametrów szablonu.
źródło
std::forward<Args>(args)...
, która rozwija się doforward<T1>(x1), forward<T2>(x2), ...
.forward
tak naprawdę zawsze wymaga parametru szablonu, prawda?Zainspirowany wdrożeniem Stephana T. Lavaveja, pomyślałem, że fajnie byłoby mieć make_unique, który obsługiwałby zakresy tablic, jest na githubie i chciałbym otrzymywać komentarze na jego temat. Pozwala to zrobić:
źródło