2017-07-27 7 views
13

std::make_unique() (और समान कार्य) के लिए make_unique/make_shared/क़ायम करना/आदि में अतिरिक्त कदम से बचना है एक छोटे से problem:संरचनाओं कि कुल आरंभीकरण का उपयोग

#include <cstdio> 
#include <memory> 

using namespace std; 

struct S 
{ 
    S()   { printf("ctor\n"); } 
    ~S()  { printf("dtor\n"); } 
    S(S const&) { printf("cctor\n"); } 
    S(S&&)  { printf("mctor\n"); } 
}; 

S foo() { return S(); } 

int main() 
{ 
    { 
     printf("--------------- case 1 ---------------\n"); 
     unique_ptr<S> s1 = make_unique<S>(foo()); 
    } 

    { 
     printf("--------------- case 2 ---------------\n"); 
     unique_ptr<S> s2 { new S(foo()) }; 
    } 
} 

आउटपुट:

--------------- case 1 --------------- 
ctor 
mctor 
dtor 
dtor 
--------------- case 2 --------------- 
ctor 
dtor 

जैसा कि आप देख हमारे पास एक अतिरिक्त कदम है जिसे टाला जा सकता है। Same समस्या वैकल्पिक/संस्करण/आदि में emplace() के साथ मौजूद है - यदि ऑब्जेक्ट अन्य फ़ंक्शन द्वारा लौटाया जाता है, तो आपको इसे स्थानांतरित करना होगा।

यह एक चाल के साथ addressed हो सकता है:

#include <cstdio> 
#include <optional> 

using namespace std; 

struct S 
{ 
    S()   { printf("ctor\n"); } 
    ~S()  { printf("dtor\n"); } 
    S(S const&) { printf("cctor\n"); } 
    S(S&&)  { printf("mctor\n"); } 

    template<class F, enable_if_t<is_same_v<invoke_result_t<F>, S>>...> 
    S(F&& f) : S(forward<F>(f)()) {} 
}; 

S foo() { return S(); } 

int main() 
{ 
    optional<S> s; 
    s.emplace([]{ return foo(); }); 
} 

यह अनावश्यक कदम से बचा जाता है (enable_if खाल निर्माता जब तक f() एस का एक उदाहरण देता है)। आप अपने निर्माण कार्य के लिए कॉल के माध्यम से std::variant/std::optional/आदि के अंदर अपने मूल्यों को प्रभावी ढंग से समाप्त कर देते हैं।

इस फिक्स में एक छोटी सी समस्या है - एक कन्स्ट्रक्टर जोड़ना कुल प्रारंभिक तोड़ता है। example देखें। अर्थात। अगर दिया संरचना कोई निर्माता था और आप एक जोड़ने - यदि आप अब इस तरह यह प्रारंभ कर सकते हैं:

struct D 
{ 
    float m; 
    S s; 

    // adding new constructor here will break existing bar() functions 
}; 

D bar() { /*...lots of code with multiple return statements...*/ return {2.0, foo()}; } 

प्रश्न: इस समस्या के चारों ओर एक रास्ता नहीं है? ऐसा कुछ जो नए रचनाकारों को पेश नहीं करता है ...

मैं अपने संरचनाओं को वैकल्पिक रूप से वैकल्पिक/भिन्न/साझा_ptr-block/etc में बिना किसी ब्रेकिंग (बल्कि गैर-तुच्छ) कोड को डालने के लिए सक्षम करना चाहता हूं जो उन्हें बनाता है।

उत्तर

9

फ़ैक्टरी फ़ंक्शन लेने वाले आपके प्रकार में एक कन्स्ट्रक्टर जोड़ने के बजाय, इसके बजाय एक रूपांतरण ऑपरेटर के साथ एक नया बाहरी फैक्ट्री ऑब्जेक्ट बनाएं। साथ सी ++ 17, कि कम से कम काम लेता है:

template <class F> 
struct factory { 
    F f; 

    operator invoke_result_t<F&>() { return f(); } 
}; 

template <class F> 
factory(F) -> factory<F>; 

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

optional<S> s; 
s.emplace(factory{[]{ return foo(); }}); // or really just factory{foo} 

कौन सा सिर्फ प्रिंट ctor और dtor। चूंकि हम किसी भी तरह से S संशोधित नहीं कर रहे हैं, इसलिए हम इसका उपयोग समेकन में भी कर सकते हैं - जैसे D

+0

आपके 'ऑपरेटर परिणाम 'में' & ',' const & 'और' && 'ओवरलोड होने के लिए एक छोटा सा मामूली लाभ हो सकता है। ध्यान दें कि इसमें एक छोटी सी गड़बड़ी है, जिसमें परिणामस्वरूप लालची सीटीआर रूपांतरण ऑपरेटर को मौका दिए बिना कारखाने का उपभोग कर सकता है, लेकिन कुल मिलाकर समस्या नहीं है। – Yakk

+0

'invoke_result_t ' (या 'वापसी std :: move (f)(); ', आपका चयन।) –

+0

@ टी.सी. मैं सिंगल-कैरेक्टर एडिट लेगा :) – Barry

संबंधित मुद्दे