make_unique ve mükemmel yönlendirme


216

std::make_uniqueStandart C ++ 11 kitaplığında neden işlev şablonu yok ? buldum

std::unique_ptr<SomeUserDefinedType> p(new SomeUserDefinedType(1, 2, 3));

biraz ayrıntılı. Aşağıdakiler çok daha hoş olmaz mıydı?

auto p = std::make_unique<SomeUserDefinedType>(1, 2, 3);

Bu newgüzel gizler ve sadece bir kez türü bahseder.

Her neyse, işte benim uygulama girişimim 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)...));
}

Bu std::forwardşeylerin derlenmesini sağlamak biraz zaman aldı , ama doğru olup olmadığından emin değilim. Bu mu? Tam olarak ne anlama std::forward<Args>(args)...geliyor? Derleyici bundan ne çıkarıyor?


1
Daha önce bu tartışmayı yaptığımızdan eminim ... ayrıca unique_ptrbir şekilde izin vermeniz gereken ikinci bir şablon parametresi aldığını unutmayın - bu farklıdır shared_ptr.
Kerrek SB

5
@Kerrek: Ben parametreleştirmenin mantıklı olacağını sanmıyorum make_uniqueaçıkçası düz eski aracılığıyla ayırdığı için, özel bir deleter ile newdolayısıyla ve gereken kullanmak düz eski delete:)
fredoverflow

1
@Fred: Bu doğru. Öyleyse, önerilen tahsis make_uniqueile sınırlı olacak new... Eh, yazmak istiyorsanız sorun değil, ama böyle bir şeyin neden standardın bir parçası olmadığını görebiliyorum.
Kerrek SB

4
Aslında, make_uniqueyapıcısı std::unique_ptraçık olduğu için bir şablon kullanmayı seviyorum ve bu nedenle unique_ptrbir işlevden dönmek ayrıntılıdır . Ayrıca, oldukça kullanmayı tercih ediyorum auto p = make_unique<foo>(bar, baz)daha std::unique_ptr<foo> p(new foo(bar, baz)).
Alexandre C.

Yanıtlar:


157

C ++ standardizasyon komitesi başkanı Herb Sutter blogunda şöyle yazıyor :

C ++ 11'in içermediği make_uniquekısmen bir gözetimdir ve gelecekte kesinlikle kesinlikle eklenecektir.

Ayrıca OP tarafından verilenle aynı olan bir uygulama da verir.

Düzenleme: std::make_unique şimdi C ++ 14'ün bir parçasıdır .


2
Bir make_uniqueişlev şablonu, istisna güvenli çağrıları garanti etmez. Arayanın kullandığı konvansiyona dayanır. Buna karşılık, katı statik tip kontrolü (C ++ ve C arasındaki ana farktır), türler yoluyla güvenliği zorlama fikri üzerine inşa edilmiştir . Ve make_uniquebunun için, işlev yerine bir sınıf olabilir. Örneğin benim bkz blog makalesinde ayrıca Herb'ün blogda tartışma adresinde bağlantısı Mayıs 2010'da.
Şerefe ve s. - Alf

1
@ DavidRodríguez-dribeas: İstisna güvenliği konusunu anlamak için Herb'in blogunu okuyun. Unutmayın "türetilmek üzere tasarlanmamış", bu sadece saçma. make_uniquebir sınıf yapmak benim önerim yerine , ben artık bir işlev üreten yapmak daha iyi olduğunu düşünüyorum make_unique_t, bunun nedeni en sapık ayrıştırma :-) ile ilgili bir sorundur.
Şerefe ve s. - Alf

1
@ Cheersandhth.-Alf: Belki farklı bir makale okuduk, çünkü az önce okuduğum makalede make_uniquegüçlü istisna garantisi olduğunu belirtiyor . Ya da belki aracı kullanımı ile karıştırıyorsunuz, bu durumda hiçbir fonksiyon istisna güvenli değildir. Düşünün void f( int *, int* ){}, no throwgarantiyi açıkça sunar , ancak akıl yürütme hattınız tarafından istismar güvenli değildir, çünkü yanlış kullanılabilir. Daha da kötüsü, void f( int, int ) {}istisna da güvenli değil !: typedef unique_ptr<int> up; f( *up(new int(5)), *up(new int(10)))...
David Rodríguez - dribeas

2
Cheersandhth.-Alf @: ben istisna sorunları hakkında sorulan içinde make_unique yukarıdaki gibi uygulanmışsa ve Sutter tarafından bir makale bana gelin, makale benim google-fu durumlarına işaret beni o make_uniqueteklifler güçlü istisna garanti yukarıdaki ifadeyi çelişmektedir. Eğer farklı bir yazı varsa ben değilim okuma ilgini. Yani benim asıl soru duruyor Nasıl make_uniqueistisna değil kasa (yukarıda tanımlandığı gibi)? (Sidenote: evet, uygulanabileceği yerlerde istisna güvenliğini make_unique geliştirdiğini düşünüyorum )
David Rodríguez - dribeas

3
... ayrıca kullanımı daha az güvenli bir make_uniqueişlev peşinde değilim . Öncelikle bunun nasıl güvensiz olduğunu görmüyorum ve ekstra bir tür eklemenin onu nasıl daha güvenli hale getireceğini görmüyorum . Ne anlarım ben ilgileniyorum olmasıdır anlayarak bu uygulama --Ben any-- göremiyorum sahip olabileceği sorunları ve alternatif uygulama bunları çözmek nasıl. Nedir konvansiyonlarmake_unique bağlıdır? Güvenliği sağlamak için tip kontrolünü nasıl kullanırsınız? Bunlar cevap vermeyi sevdiğim iki soru.
David Rodríguez - dribeas

78

Güzel, ama Stephan T. Lavavej (daha iyi STL olarak bilinir), make_uniquedizi sürümü için doğru çalışan daha iyi bir çözüme sahiptir .

#include <memory>
#include <type_traits>
#include <utility>

template <typename T, typename... Args>
std::unique_ptr<T> make_unique_helper(std::false_type, Args&&... args) {
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

template <typename T, typename... Args>
std::unique_ptr<T> make_unique_helper(std::true_type, Args&&... args) {
   static_assert(std::extent<T>::value == 0,
       "make_unique<T[N]>() is forbidden, please use make_unique<T[]>().");

   typedef typename std::remove_extent<T>::type U;
   return std::unique_ptr<T>(new U[sizeof...(Args)]{std::forward<Args>(args)...});
}

template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
   return make_unique_helper<T>(std::is_array<T>(), std::forward<Args>(args)...);
}

Bu onun Core C ++ 6 videosunda görülebilir .

Make_unique ait STL? Sürümüne güncellenmiş sürümü olarak kullanıma sunulmuştur N3656 . Bu sürüm taslak C ++ 14'e uyarlanmıştır .


make_unique Stephen T Lavalej tarafından bir sonraki standart güncellemede önerilmiştir.
tominator

İşte ona ekleme hakkında konuşmaya benzer. channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Core-C-/…
tominator

neden gereksiz tüm düzenlemeleri xeo yapmak, olduğu gibi iyiydi. Bu kod Stephen T. Lavalej'in söylediği gibi aynıydı ve std kütüphanesini koruyan dinkumware için çalışıyor. O duvara zaten yorum yaptın, bilmelisin.
tominator

3
İçin uygulama make_uniquebir başlık içinde olmalıdır. Üstbilgiler bir ad alanını içe aktarmamalıdır (Sutter / Alexandrescu'nun "C ++ Kodlama Standartları" kitabındaki 59. maddeye bakın). Xeo'nun değişiklikleri kötü uygulamaları teşvik etmekten kaçınmaya yardımcı olur.
Bret Kuhns

ne yazık ki, her ikisi de varidic tempalte parametresini desteklemeyen VC2010, hatta VC2012 tarafından desteklenmiyor
zhaorufei

19

std::make_sharedsadece kısayol değildir std::shared_ptr<Type> ptr(new Type(...));. Onsuz yapamayacağınız bir şey yapar .

İşini yapmak std::shared_ptriçin, gerçek işaretçi için depolama alanını tutmanın yanı sıra bir izleme bloğu ayırmalıdır. Bununla birlikte, std::make_sharedtahsis gerçek nesne, bu mümkün olduğu std::make_sharedayırır konusu, her ikisi ve de izleme bloğunu aynı bellek bloğuna .

Yani ederken std::shared_ptr<Type> ptr = new Type(...);iki bellek ayırmalarını (için biri olacağını new, bir std::shared_ptrizleme bloğu), std::make_shared<Type>(...)tahsis edecek birini bellek bloğu.

Bu, birçok potansiyel kullanıcı için önemlidir std::shared_ptr. std::make_uniqueA'nın yapacağı tek şey biraz daha uygun olmaktır. Bundan daha fazlası yok.


2
Gerekli değil. İma etti, ama gerekli değil.
Köpek yavrusu

3
O olmayacağım sadece kolaylık olması için, aynı zamanda bazı durumlarda istisna güvenliğini artıracak. Bunun için Kerrek SB'nin cevabına bakınız.
Piotr99

19

Hiçbir şey kendi yardımcısını yazmanızı engellemese make_shared<T>de, kütüphanede sağlamanın ana nedeninin aslında shared_ptr<T>(new T)farklı bir şekilde tahsis edilmiş olandan farklı bir iç paylaşılan işaretçi oluşturması ve buna adanmış olmadan bunu başarmanın bir yolu olmadığına inanıyorum. yardımcı.

Sizin make_uniquediğer taraftan sarıcı bir çevrede sadece sözdizimsel şeker newo, hiçbir şey getirmiyor göze hoş görünebilir bu yüzden ederken, ifade newmasaya. Düzeltme: bu aslında doğru değildir: newİfadeyi sarmak için bir işlev çağrısına sahip olmak , örneğin bir işlevi çağırmanız durumunda istisna güvenliği sağlar void f(std::unique_ptr<A> &&, std::unique_ptr<B> &&). newBirbirine göre birbirinden farklı iki ham madde olması, yeni bir ifade bir istisna dışında başarısız olursa, diğerinin kaynakları sızdırabileceği anlamına gelir. make_uniqueStandartta neden yok diye gelince : Sadece unutuldu. (Bu arada sırada olur. Küresel de yokturstd::cbegin sırada gerçekleşir. Standart olmasına rağmen .)

Ayrıca unique_ptr, bir şekilde izin vermeniz gereken ikinci bir şablon parametresi aldığını unutmayın ; bu, özel silgileri türün bir parçası haline getirmeden saklamakshared_ptr için tür silme özelliğini kullananlardan farklıdır .


1
@FredOverflow: Paylaşılan işaretçi nispeten karmaşık bir sınıftır; dahili olarak bir polimorfik referans kontrol bloğunu tutar, ancak birkaç farklı kontrol bloğu türü vardır. shared_ptr<T>(new T)bunlardan birini make_shared<T>()kullanır, farklı bir tane kullanır. Buna izin vermek İyi Bir Şeydir ve paylaşılan sürüm bir anlamda alabileceğiniz en hafif paylaşılan işaretçidir.
Kerrek SB

15
@FredOverflow: Sayıyı shared_ptrve "oluşturucu" eylemini oluştururken bir dinamik bellek bloğu atar shared_ptr. İşaretçiyi açık bir şekilde iletirseniz, "yeni" bir blok oluşturması gerekir; eğer kullanırsanız , nesnenizi ve uydu verilerinizi tek bir bellek bloğunda (bir ) make_sharedpaketleyebilir ( daha hızlı tahsis / ayrılma, daha az parçalanma ve (normal olarak) ) daha iyi önbellek davranışı. new
Matthieu M.


4
-1 "Öte yandan make_unique ambalajınız sadece yeni bir ifade etrafında sözdizimsel şekerdir, bu yüzden göze hoş gelse de masaya yeni bir şey getirmez." Hata. İstisna güvenli işlev çağırma olasılığını getirir. Ancak bir garanti getirmez; bunun için sınıf olması gerekir, böylece resmi argümanlar bu sınıftan ilan edilebilir (Herb bunu katılma ve ayrılma arasındaki fark olarak tanımladı).
Şerefe ve s. - Alf

1
@ Cheersandhth.-Alf: Evet, bu doğru. O zamandan beri bunu fark ettim. Cevabı düzenleyeceğim.
Kerrek SB

13

C ++ 11'de ... (şablon kodunda) "paket genişletme" için de kullanılır.

Gereksinim, genişlememiş bir parametre paketi içeren bir ifadenin soneki olarak kullanmanız ve ifadeyi paketin öğelerinin her birine uygulayabilmesidir.

Örneğin, örneğinizi temel alarak:

std::forward<Args>(args)... -> std::forward<int>(1), std::forward<int>(2),
                                                     std::forward<int>(3)

std::forward<Args...>(args...) -> std::forward<int, int, int>(1,2,3)

İkincisinin yanlış olduğunu düşünüyorum.

Ayrıca, argüman paketi genişletilmemiş bir işleve geçirilemez. Bir şablon parametreleri paketi hakkında emin değilim.


1
Bunu dile getirdiđime sevindim. Neden varyasyon şablonu parametresini de eklemiyorsunuz?
Kerrek SB

@Kerrek: Çünkü garip sözdiziminden pek emin değilim ve birçok insanın oynayıp oynamadığını bilmiyorum. Böylece bildiklerimi koruyacağım. Biri yeterince motivasyona sahip ve bilgili ise, c varyasyon sözdizimi oldukça eksiksiz olduğundan, bir c ++ SSS girişi gerektirebilir.
Matthieu M.

Sözdizimi, std::forward<Args>(args)...genişler forward<T1>(x1), forward<T2>(x2), ....
Kerrek SB

1
: forwardAslında her zaman bir şablon parametresi gerektirir, değil mi?
Kerrek SB

1
@Howard: doğru! Zorlama örneği uyarıyı tetikler ( ideone.com/GDNHb )
Matthieu M.

5

Stephan T. Lavavej tarafından uygulanması esinlenerek, Bence dizi kapsamlarını desteklenen bir make_unique olması güzel olabileceğini düşündüm o github üzerinde olduğunu ve bunu ilgili yorum almak isteriz. Bunu yapmanıza izin verir:

// create unique_ptr to an array of 100 integers
auto a = make_unique<int[100]>();

// create a unique_ptr to an array of 100 integers and
// set the first three elements to 1,2,3
auto b = make_unique<int[100]>(1,2,3); 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.