make_unique और परफेक्ट फॉरवर्डिंग


216

std::make_uniqueमानक C ++ 11 लाइब्रेरी में कोई फ़ंक्शन टेम्पलेट क्यों नहीं है ? मुझे लगता है

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

थोड़ी क्रिया। निम्नलिखित बहुत अच्छा नहीं होगा?

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

यह newअच्छी तरह से छुपाता है और केवल एक बार प्रकार का उल्लेख करता है।

वैसे भी, यहाँ मेरा प्रयास है 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)...));
}

std::forwardसामान संकलित करने में मुझे काफी समय लगा , लेकिन अगर यह सही है तो मुझे यकीन नहीं है। क्या यह? वास्तव में क्या std::forward<Args>(args)...मतलब है? संकलक क्या करता है?


1
बहुत यकीन है कि हम पहले भी इस चर्चा कर चुके हैं ... यह भी ध्यान दें कि unique_ptrएक दूसरा टेम्पलेट पैरामीटर लेता है जिसे आपको किसी भी तरह से अनुमति देना चाहिए - वह इससे अलग है shared_ptr
केरेक एसबी

5
@ केरैक: मुझे नहीं लगता कि यह make_uniqueएक कस्टम डिलेटर के साथ पैरामीटर बनाने के लिए समझ में आता है , क्योंकि स्पष्ट रूप से यह सादे पुराने के माध्यम से आवंटित होता है newऔर इसलिए सादे पुराने का उपयोग करना चाहिएdelete :)
fredoverflow

1
@ फ़्रेड: यह सच है। इसलिए, प्रस्तावित आवंटन make_uniqueतक सीमित होगा new... ठीक है, यह ठीक है यदि आप इसे लिखना चाहते हैं, लेकिन मैं देख सकता हूं कि ऐसा कुछ क्यों मानक का हिस्सा नहीं है।
केरेक एसबी

4
असल में, मैं एक make_uniqueटेम्पलेट का उपयोग करना पसंद करता हूं क्योंकि कंस्ट्रक्टर std::unique_ptrस्पष्ट है, और इस प्रकार यह unique_ptrएक फ़ंक्शन से लौटने के लिए क्रिया है। इसके अलावा, मैं नहीं बल्कि प्रयोग करेंगे auto p = make_unique<foo>(bar, baz)की तुलना में std::unique_ptr<foo> p(new foo(bar, baz))
अलेक्जेंड्रे सी।

5
make_uniqueमें आ रहा है C++14, isocpp.org/blog/2013/04/trip-report-iso-c-spring-2013-meeting
stefan

जवाबों:


157

हर्ब सटर, सी ++ मानकीकरण समिति के अध्यक्ष, अपने ब्लॉग पर लिखते हैं :

यह C ++ 11 make_uniqueमें आंशिक रूप से शामिल नहीं है, और यह भविष्य में लगभग निश्चित रूप से जोड़ा जाएगा।

वह एक कार्यान्वयन भी देता है जो ओपी द्वारा दिए गए एक के समान है।

संपादित करें: std::make_unique अब C ++ 14 का हिस्सा है ।


2
एक make_uniqueफ़ंक्शन टेम्पलेट स्वयं अपवाद को सुरक्षित कॉल की गारंटी नहीं देता है। यह सम्मेलन पर निर्भर करता है, कि कॉलर इसका उपयोग करता है। इसके विपरीत, सख्त स्थैतिक प्रकार की जाँच (जो C ++ और C के बीच मुख्य अंतर है) को सुरक्षा के विचार पर बनाया गया है , प्रकारों के माध्यम से। और उसके लिए, make_uniqueफ़ंक्शन के बजाय बस एक वर्ग हो सकता है। उदाहरण के लिए, मई 2010 से मेरा ब्लॉग लेख देखें । यह हर्ब के ब्लॉग पर चर्चा से भी जुड़ा है।
चीयर्स एंड हीथ। - अल्फ

1
@ DavidRodríguez-dribeas: अपवाद सुरक्षा मुद्दे को समझने के लिए हर्ब के ब्लॉग को पढ़ें। "व्युत्पन्न होने के लिए डिज़ाइन नहीं किया गया" के बारे में भूल जाओ, यह सिर्फ बकवास है। make_uniqueएक कक्षा बनाने के मेरे सुझाव के बजाय , अब मुझे लगता है कि इसे एक ऐसा फ़ंक्शन बनाना बेहतर है जो एक उत्पादन करता है make_unique_t, इसका कारण सबसे विकृत पार्स :-) के साथ एक समस्या है।
चीयर्स एंड हीथ। - अल्फ

1
@ चेरसन्ध।-अल्फ: हो सकता है कि हमने एक अलग लेख पढ़ा हो, क्योंकि जो मैंने अभी पढ़ा है वह स्पष्ट रूप से बताता है कि make_uniqueमजबूत अपवाद की गारंटी देता है। या हो सकता है कि आप टूल को इसके उपयोग के साथ मिला रहे हों, जिस स्थिति में कोई फ़ंक्शन अपवाद सुरक्षित नहीं है। विचार करें void f( int *, int* ){}, स्पष्ट रूप से no throwगारंटी प्रदान करता है , लेकिन आपके तर्क की रेखा से यह सुरक्षित नहीं है, क्योंकि इसका दुरुपयोग किया जा सकता है। इससे भी बदतर, void f( int, int ) {}अपवाद सुरक्षित नहीं है !: typedef unique_ptr<int> up; f( *up(new int(5)), *up(new int(10)))...
डेविड रॉड्रिग्ज - dribeas

2
@ Cheersandhth.-Alf: मैंने ऊपर के रूप में make_unique कार्यान्वित किए गए अपवाद मुद्दों के बारे में पूछा और आपने मुझे Sutter द्वारा एक लेख की ओर इशारा किया, यह लेख जो मेरे google-fu ने मुझे बताया था कि make_uniqueयह मजबूत अपवाद गारंटी प्रदान करता है , जो आपके कथन का खंडन करता है। यदि आपके पास एक अलग लेख है तो मुझे इसे पढ़ने में दिलचस्पी है। तो मेरा मूल प्रश्न यह है कि कैसे make_unique(जैसा कि ऊपर परिभाषित किया गया है) अपवाद सुरक्षित नहीं है? (सिडेनोट: हाँ, मुझे लगता है कि यह उन जगहों पर अपवाद सुरक्षा में make_unique सुधार करता है जहाँ इसे लागू किया जा सकता है)
डेविड रोड्रिग्ज़ - dribeas

3
... यह भी मैं make_uniqueसमारोह का उपयोग करने के लिए एक कम सुरक्षित पीछा नहीं कर रहा हूँ । पहले मैं नहीं देखता कि यह कैसे असुरक्षित है, और मैं नहीं देखता कि कैसे एक अतिरिक्त प्रकार को जोड़ने से यह सुरक्षित हो जाएगा । मुझे क्या पता है कि मुझे उन मुद्दों को समझने में दिलचस्पी है जो इस कार्यान्वयन में हो सकते हैं - मैं कोई भी नहीं देख सकता - और कैसे एक वैकल्पिक कार्यान्वयन उन्हें हल करेगा। ऐसे कौन से सम्मेलन हैं जो make_uniqueनिर्भर करते हैं? सुरक्षा को लागू करने के लिए आप किस प्रकार की जाँच का उपयोग करेंगे? वे दो सवाल हैं जिनके लिए मुझे एक जवाब पसंद आएगा।
डेविड रॉड्रिग्ज़ - drieaseas

78

अच्छा है, लेकिन Stephan T. Lavavej (जिसे STL के रूप में जाना जाता है) के लिए एक बेहतर समाधान है make_unique, जो सरणी संस्करण के लिए सही तरीके से काम करता है।

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

यह उनके कोर सी ++ 6 वीडियो पर देखा जा सकता है ।

STL के संस्करण का अपडेटेड संस्करण make_unique अब N3656 के रूप में उपलब्ध है । यह संस्करण C ++ 14 के मसौदे में अपनाया गया।


make_unique को स्टीफन टी लवलेज ने अगले एसटीडी अपडेट में प्रस्तावित किया है।
स्मारिका

यहाँ उसे जोड़ने के बारे में बात करने की तरह है। Channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Core-C-/-…
tominator

क्यों सभी अनावश्यक संपादन xeo बनाते हैं, यह ठीक था जैसा कि यह था। वह कोड बिल्कुल वैसा ही था जैसा स्टीफन टी। लवलेज ने डाला था, और वह डिंकुमवेयर के लिए काम करता है जो एसटीडी लाइब्रेरी को बनाए रखता है। आपने पहले ही उस दीवार पर टिप्पणी की है, इसलिए आपको पता होना चाहिए।
स्मारिका

3
के लिए कार्यान्वयन make_uniqueहेडर में जाना चाहिए। हेडर्स को एक नेमस्पेस आयात नहीं करना चाहिए (देखें आइटम 59 # सटर / अलेक्जेंड्रेस्कु के "C ++ कोडिंग स्टैंडर्ड्स" बुक में)। Xeo के परिवर्तन बुरी प्रथाओं को प्रोत्साहित करने से बचने में मदद करते हैं।
ब्रेट कुह्न

दुर्भाग्य से, VC2010 द्वारा समर्थित नहीं, यहां तक ​​कि VC2012 मुझे भी लगता है, जो दोनों वैरिएड टेम्पल पैरामीटर का समर्थन नहीं करते हैं
zhaorufei

19

std::make_sharedके लिए सिर्फ आशुलिपि नहीं है std::shared_ptr<Type> ptr(new Type(...));। यह ऐसा कुछ करता है जो आप इसके बिना नहीं कर सकते

अपना काम std::shared_ptrकरने के लिए, वास्तविक पॉइंटर के लिए भंडारण रखने के अलावा एक ट्रैकिंग ब्लॉक आवंटित करना चाहिए। हालाँकि, क्योंकि std::make_sharedवास्तविक ऑब्जेक्ट को आवंटित करता है, यह संभव है कि मेमोरी के एक ही ब्लॉक में std::make_sharedऑब्जेक्ट और ट्रैकिंग ब्लॉक दोनों को आवंटित करता है ।

तो जबकि std::shared_ptr<Type> ptr = new Type(...);दो मेमोरी एलोकेशन होंगे (एक new, std::shared_ptrट्रैकिंग ब्लॉक में से एक ), मेमोरी के एक ब्लॉक std::make_shared<Type>(...)को आवंटित करेगा ।

यह कई संभावित उपयोगकर्ताओं के लिए महत्वपूर्ण है std::shared_ptr। केवल एक ही चीज़ std::make_uniqueथोड़ी अधिक सुविधाजनक होगी। इससे ज्यादा कुछ नहीं।


2
इसकी आवश्यकता नहीं है। संकेत दिया, लेकिन आवश्यक नहीं।
पिल्ला

3
यह केवल सुविधा के लिए नहीं है , यह कुछ मामलों में अपवाद सुरक्षा में भी सुधार करेगा। उसके लिए केरेबेक एसबी का जवाब देखें।
पियोत्र 99

19

जबकि कुछ भी आपको अपने स्वयं के सहायक लिखने से रोकता है, मेरा मानना ​​है कि make_shared<T>पुस्तकालय में प्रदान करने का मुख्य कारण यह है कि यह वास्तव में एक अलग आंतरिक प्रकार के साझा सूचक की तुलना में बनाता है shared_ptr<T>(new T), जो कि अलग-अलग आवंटित किया गया है, और समर्पित के बिना इसे प्राप्त करने का कोई तरीका नहीं है। सहायक।

make_uniqueदूसरी ओर आपका आवरण एक newअभिव्यक्ति के आसपास मात्र चीनी है , इसलिए जब यह आंख को प्रसन्न कर सकता है, तो यह newमेज पर कुछ भी नहीं लाता है । सुधार: यह वास्तव में सही नहीं है: newअभिव्यक्ति को लपेटने के लिए फ़ंक्शन कॉल होने से अपवाद सुरक्षा मिलती है, उदाहरण के लिए उस मामले में जहां आप एक फ़ंक्शन कहते हैं void f(std::unique_ptr<A> &&, std::unique_ptr<B> &&)। दो कच्चे newएस होने के कारण जो एक दूसरे के संबंध में अप्रभावित हैं, इसका मतलब है कि यदि एक अपवाद के साथ एक नई अभिव्यक्ति विफल हो जाती है, तो दूसरा संसाधनों को लीक कर सकता है। जैसे कि make_uniqueमानक में कोई क्यों नहीं है: यह अभी भूल गया था। (यह कभी-कभार होता है। std::cbeginमानक में कोई वैश्विक नहीं है भले ही एक होना चाहिए।)

यह भी ध्यान दें कि unique_ptrएक दूसरा टेम्पलेट पैरामीटर लेता है जिसे आपको किसी भी तरह से अनुमति देना चाहिए; यह अलग है shared_ptr, जो कस्टम डिलीटर्स को स्टोर करने के लिए टाइप एस्ट्रस का उपयोग करता है, उन्हें टाइप का हिस्सा बनाए बिना।


1
@FredOverflow: साझा सूचक एक अपेक्षाकृत जटिल वर्ग है; आंतरिक रूप से यह एक बहुरूपी संदर्भ नियंत्रण ब्लॉक रखता है, लेकिन कई अलग-अलग प्रकार के नियंत्रण ब्लॉक हैं। shared_ptr<T>(new T)उनमें से एक का उपयोग करता है, make_shared<T>()एक अलग का उपयोग करता है। अनुमति है कि एक अच्छी बात है, और मेक-साझा संस्करण कुछ मायने में सबसे हल्का वजन साझा सूचक है जिसे आप प्राप्त कर सकते हैं।
केरेक एसबी

15
@FredOverflow: shared_ptrजब आप एक बनाते हैं तो गिनती और "डिस्पोज़र" कार्रवाई को बनाए रखने के लिए गतिशील मेमोरी का एक ब्लॉक आवंटित करता है shared_ptr। यदि आप सूचक को स्पष्ट रूप से पास करते हैं, तो उसे "नया" ब्लॉक बनाने की आवश्यकता होती है, यदि आप इसका उपयोग make_sharedकरते हैं तो यह आपकी वस्तु और उपग्रह डेटा को मेमोरी के एकल ब्लॉक (एक new) में बंडल कर सकता है जिसके परिणामस्वरूप तेजी से आवंटन / डील्लोकेशन, कम विखंडन, और (सामान्य रूप से) ) बेहतर कैश व्यवहार।
मैथ्यू एम।

2
मुझे लगता है कि मुझे
share_ptr

4
-1 "दूसरी ओर आपका मेक_निक रैपर एक नई अभिव्यक्ति के आसपास मात्र शक्करयुक्त चीनी है, इसलिए जब यह आंख को प्रसन्न कर सकता है, तो यह मेज पर कुछ भी नया नहीं लाता है।" गलत है। यह अपवाद सुरक्षित फ़ंक्शन मंगलाचरण की संभावना लाता है। हालाँकि, यह गारंटी नहीं लाता है; उसके लिए, इसे कक्षा में रखने की आवश्यकता होगी ताकि औपचारिक तर्क को उस वर्ग के रूप में घोषित किया जा सके (हर्ब ने बताया कि ऑप्ट-इन और ऑप्ट-आउट के बीच अंतर के रूप में)।
चीयर्स एंड हीथ। - अल्फ

1
@ चेरसन्ध।-अल्फ: हाँ, यह सच है। जब से मुझे इसका एहसास हुआ है। मैं उत्तर संपादित करूँगा।
केरेक एसबी

13

..."पैक विस्तार" के लिए C ++ 11 में (टेम्प्लेट कोड में) उपयोग किया जाता है।

आवश्यकता इस बात की है कि आप इसे एक अनएक्सपेन्डेन्ड पैक के मापदंडों के एक अभिव्यक्ति के प्रत्यय के रूप में उपयोग करते हैं, और यह बस पैक के प्रत्येक तत्व के लिए अभिव्यक्ति को लागू करेगा।

उदाहरण के लिए, अपने उदाहरण पर निर्माण:

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)

बाद वाला गलत है मुझे लगता है।

इसके अलावा, तर्कों का पैक बिना किसी फ़ंक्शन के पास नहीं हो सकता है। मैं टेम्पलेट मापदंडों के एक पैक के बारे में अनिश्चित हूं।


1
यह देखकर अच्छा लगा। वैरेडिक टेम्प्लेट पैरामीटर को भी शामिल क्यों नहीं किया जाता है?
केरेक एसबी

@ केरैक: क्योंकि मैं इसके अजीब सिंटैक्स के बारे में निश्चित नहीं हूं, और मुझे नहीं पता कि कई लोगों के साथ खेला है। इसलिए मैं जो जानता हूं उसे रखूंगा। यह c ++ FAQ प्रविष्टि को वारंट कर सकता है, अगर कोई व्यक्ति प्रेरित और जानकार था, तो वैरिएडिक सिंटैक्स के लिए काफी पूर्ण है।
मैथ्यू एम।

वाक्य-विन्यास है std::forward<Args>(args)..., जिसका विस्तार होता है forward<T1>(x1), forward<T2>(x2), ...
केरेक एसबी

1
: मुझे लगता है कि forwardवास्तव में हमेशा एक टेम्पलेट पैरामीटर की आवश्यकता होती है, है ना?
केरेक एसबी

1
@ हॉवर्ड: सही है! तात्कालिकता चेतावनी को ट्रिगर करता है ( ideone.com/GDNHb )
मैथ्यू एम।

5

Stephan T. Lavavej के कार्यान्वयन से प्रेरित होकर, मैंने सोचा कि एक ऐसा मेक_उनीक होना अच्छा हो सकता है जो सरणी विलुप्त होने का समर्थन करता हो, यह github पर है और मुझे इस पर टिप्पणियां प्राप्त करना अच्छा लगेगा। यह आपको ऐसा करने की अनुमति देता है:

// 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); 
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.