2016-03-10 5 views
15

के रूप में स्थानीय अद्वितीय_ptr लौटने पर मुझे का उपयोग नहीं करने के लिए std::unique_ptr का उपयोग करने के लिए उपयोग नहीं किया जाता है, क्योंकि ऐसा आरवीओ को प्रतिबंधित करता है। मेरे पास यह मामला है जहां मेरे पास स्थानीय std::unique_ptr है, लेकिन वापसी का प्रकार std::shared_ptr है। यहाँ कोड का एक नमूना है:एक साझा_ptr

shared_ptr<int> getInt1() { 
    auto i = make_unique<int>(); 

    *i = 1; 

    return i; 
} 

shared_ptr<int> getInt2() { 
    return make_unique<int>(2); 
} 

unique_ptr<int> getInt3() { 
    auto ptr = make_unique<int>(2); 

    return ptr; 
} 

int main() { 
    cout << *getInt1() << endl << *getInt2() << *getInt3() << endl; 
    return 0; 
} 

जीसीसी दोनों ही मामलों को स्वीकार करता है, लेकिन बजना getInt1() मना कर दिया इस त्रुटि के साथ:

main.cpp:10:13: error: no viable conversion from 'std::unique_ptr<int, std::default_delete<int> >' to 'shared_ptr<int>' 
    return i; 
     ^

यहाँ coliru पर दोनों ही मामलों है: GCC, Clang

दोनों संकलक तीसरे मामले को स्वीकार करते हैं।

कौन सा गलत है? धन्यवाद।

+2

मुझे संदेह है कि हम बेहतर जवाब देने में सक्षम होंगे जब हम समझेंगे कि आपको इसके लिए क्या चाहिए। फिलहाल, इसे make_unique के बजाय make_shared को कॉल करके हल किया जा सकता है। मुझे लगता है कि ऐसा कुछ कारण है कि यह संभव नहीं है, और यह उस कारण को समझने में मदद करेगा। – Elliott

+7

@Elliott: क्या हल किया जा सकता है? वह कुछ भी हल करने के लिए नहीं पूछ रहा है, वह पूछ रहा है कि उसके पास कोड सही है या नहीं। –

उत्तर

17

सही उत्तर इस बात पर निर्भर करता है कि आप किस सी ++ मानक के बारे में बात कर रहे हैं।

यदि हम सी ++ 11 के बारे में बात कर रहे हैं, तो क्लैंग सही है (एक स्पष्ट कदम की आवश्यकता है)। अगर हम सी ++ 14 के बारे में बात कर रहे हैं, तो जीसीसी सही है (एक स्पष्ट कदम की आवश्यकता नहीं है)।

सी ++ 11 N3290/[class.copy]/p32 में कहते हैं:

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, ...

यह मांग करती है जब वापसी अभिव्यक्ति समारोह वापसी प्रकार के रूप में एक ही प्रकार है कि आप केवल निहित चाल मिलता है।

लेकिन CWG 1579 ने इसे बदल दिया, और यह दोष रिपोर्ट सी ++ 11 के बाद स्वीकार की गई, और सी ++ 14 के समय में। यह वही पैरा अब पढ़ता है:

When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If the first overload resolution fails or was not performed, ...

इस संशोधन मूल रूप से वापसी अभिव्यक्ति प्रकार की अनुमति देता है परिवर्तनीय-टू समारोह वापसी प्रकार और अभी भी निहित चाल के लिए योग्य हो।

इसका मतलब यह है कि कोड की जरूरत है एक #if/#else__cplusplus के मूल्य के आधार?

कोई ऐसा कर सकता है, लेकिन मुझे परेशान नहीं होगा।अगर मैं सी ++ 14 लक्षित कर रहे थे, मैं सिर्फ होगा: कोड अप्रत्याशित रूप से एक सी ++ 11 संकलक के तहत चलाया जाता है

return i; 

हैं, तो आप त्रुटि के संकलन समय पर सूचित कर दिया जाएगा, और यह मामूली बात है ठीक करने के लिए:

return std::move(i); 

तुम सिर्फ सी ++ 11 को लक्षित कर रहे हैं, तो move का उपयोग करें।

तुम दोनों सी ++ 11 और सी ++ 14 (और आगे) को लक्षित करना चाहते हैं, तो move का उपयोग करें। move अनावश्यक रूप से उपयोग करने का नकारात्मक पक्ष यह है कि आप RVO (वापसी मूल्य अनुकूलन) को बाधित कर सकते हैं। हालांकि, इस मामले में, RVO भी नहीं कानूनी (समारोह की वापसी प्रकार के return बयान से रूपांतरण की वजह से) है। और इसलिए अनावश्यक move कुछ भी चोट नहीं करता है।

एक बार जब आप एक नि: शुल्क move की ओर झुक सकता है यहां तक ​​कि जब सी ++ 14 को लक्षित करता है, तो इसके बिना, चीज़ें अभी भी में सी ++ 11 संकलन, और एक महंगी प्रतिलिपि रूपांतरण आह्वान, के रूप में एक कदम का विरोध किया है रूपांतरण। इस मामले में, गलती से सी ++ 11 के तहत संकलन एक मूक प्रदर्शन बग पेश करेगा। और जब सी ++ 14 नि: शुल्क move के तहत संकलित अभी भी कोई हानिकारक प्रभाव पड़ता है।

+0

@TobySpeight: संबोधित, धन्यवाद। –

+0

यदि आप केवल सी ++ 11 का उपयोग कर रहे हैं, तो मैं आपके कोड में "वापसी std :: move (..." होने के बजाय लौटने से पहले सही प्रकार बनाने के लिए प्रोत्साहित करता हूं। क्योंकि तब संकलक एक स्थानांतरित करने के बजाय एनआरवीओ कर सकता है कुछ बार। – Daemin

9

std::unique_ptr का उपयोग केवल std::shared_ptr बनाने के लिए किया जा सकता है जब यह एक रावल्यू है। std::shared_ptr के निर्माता घोषणा देखें:

template< class Y, class Deleter > 
shared_ptr(std::unique_ptr<Y,Deleter>&& r); 

तो तुम अन्यथा यह विफल करना चाहिए, std::move उपयोग करने के लिए 1 मामले के काम करने की जरूरत है।

return std::move(i); 

Btw: मैं gcc 4.9.3 साथ कोड संकलित यह या तो असफल रहा।

source_file.cpp:14:12: error: cannot bind ‘std::unique_ptr<int, std::default_delete<int> >’ 
lvalue to ‘std::unique_ptr<int, std::default_delete<int> >&&’ 
    return i; 
      ^
+0

हाय, क्या आप यह निर्दिष्ट करने के लिए अपना उत्तर बदल सकते हैं कि केवल 'C++ 11' के लिए चाल की आवश्यकता है? –

+0

यह संबंधित हो सकता है http://stackoverflow.com/q/18889843/985296 – stefan

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