2010-11-10 13 views
32

आज रात मैं कुछ कोड देख रहा हूं जो मैं पिछले कुछ दिनों में काम कर रहा हूं, और विशेष रूप से std :: move पर चलने वाले अर्थशास्त्र पर पढ़ना शुरू कर दिया। मेरे पास यह सुनिश्चित करने के लिए पेशेवरों से पूछने के लिए कुछ प्रश्न हैं कि मैं सही रास्ते पर जा रहा हूं और कोई बेवकूफ धारणा नहीं कर रहा हूं!क्या यह सी ++ 'चाल' अर्थशास्त्र का सही उपयोग है?

सबसे पहले:

1) मूल रूप से, मेरे कोड एक समारोह है कि एक बड़ी वेक्टर लौटे था:

template<class T> class MyObject 
{ 
public: 
    std::vector<T> doSomething() const; 
    { 
     std::vector<T> theVector; 

     // produce/work with a vector right here 

     return(theVector); 
    }; // eo doSomething 
}; // eo class MyObject 

को देखते हुए "theVector" इस में अस्थायी है और "फेंक-दूर", मैं फ़ंक्शन को संशोधित किया गया:

std::vector<T>&& doSomething() const; 
    { 
     std::vector<T> theVector; 

     // produce/work with a vector right here 

     return(static_cast<std::vector<T>&&>(theVector)); 
    }; // eo doSomething 

क्या यह सही है? इस तरह से ऐसा करने में कोई समस्या है?

2) मैंने देखा है कि मेरे पास एक फ़ंक्शन में std::string है जो इसे स्वचालित रूप से चालक कन्स्ट्रक्टर कहलाता है। स्ट्रिंग की वापसी (धन्यवाद, Aragorn) में डिबगिंग, मैंने देखा कि इसे एक स्पष्ट चाल कन्स्ट्रक्टर कहा जाता है। स्ट्रिंग क्लास के लिए एक और वेक्टर क्यों नहीं है?

मैं इस कदम अर्थ विज्ञान का लाभ लेने के लिए इस समारोह के लिए कोई संशोधन करना नहीं था:

// below, no need for std::string&& return value? 
std::string AnyConverter::toString(const boost::any& _val) const 
{ 
    string ret; 
    // convert here 
    return(ret); // No need for static_cast<std::string&&> ? 
}; // eo toString 

3) अंत में, मैं कुछ प्रदर्शन परीक्षण करना चाहता था, आश्चर्यजनक तेजी से परिणाम है मुझे std :: move semantics के कारण मिला है या मेरे कंपाइलर (वीएस -2010) ने कुछ अनुकूलन भी किया है?

(_getMilliseconds() का कार्यान्वयन संक्षिप्तता के लिए छोड़े गए)

std::vector<int> v; 
for(int a(0); a < 1000000; ++a) 
    v.push_back(a); 

std::vector<int> x; 
for(int a(0); a < 1000000; ++a) 
    x.push_back(a); 

    int s1 = _getMilliseconds(); 
std::vector<int> v2 = v; 
    int s2 = _getMilliseconds(); 
std::vector<int> v3 = std::move(x); 
    int s3 = _getMilliseconds(); 

    int result1 = s2 - s1; 
    int result2 = s3 - s2; 

परिणाम थे, जाहिर है, भयानक। परिणाम 1, एक मानक असाइनमेंट, 630ms लिया। दूसरा परिणाम, 0ms था। क्या यह इन चीजों का अच्छा प्रदर्शन परीक्षण है?

मुझे पता है कि इनमें से कुछ आप के लिए स्पष्ट हैं, लेकिन मैं यह सुनिश्चित करना चाहता हूं कि मैं अपने कोड पर ब्लेज़र जाने से ठीक पहले अर्थशास्त्र को समझूं।

अग्रिम धन्यवाद!

+4

आप 'std :: move' के बजाय' static_cast 'का उपयोग क्यों कर रहे हैं? – GManNickG

+0

@GMan - यही कारण है कि मैं इन प्रश्नों से पूछ रहा हूं। मेरी प्रेरणा मूल रूप से यहां से आई थी: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1377.htm#string प्रेरणा जो मैंने स्वीकार किया है कि मैंने गलत पढ़ा होगा, इसलिए यह पोस्ट :) –

+0

यदि आप वास्तव में राजस्व सीखना चाहते हैं तो मैंने आपके जवाब के लिए एक लिंक जोड़ा है। – GManNickG

उत्तर

37

एक संदर्भ अभी भी एक संदर्भ है: return एक समारोह नहीं है। उसी तरह आप सी ++ 03 (या यूबी प्राप्त करते हैं) में किसी स्थानीय के संदर्भ को वापस नहीं कर सकते हैं, आप सी ++ 0x में नहीं हो सकते हैं। आप एक मृत वस्तु के संदर्भ के साथ खत्म हो जाएगा; यह सिर्फ एक rvalue संदर्भ होने के लिए होता है। तो यह गलत है:

std::vector<T>&& doSomething() const 
{ 
    std::vector<T> local; 

    return local; // oops 
    return std::move(local); // also oops 
} 

तुम बस क्या आप नंबर दो में देखा करना चाहिए:

// okay, return by-value 
std::vector<T> doSomething() const 
{ 
    std::vector<T> local; 

    return local; // exactly the same as: 
    return std::move(local); // move-construct value 
} 

चर एक समारोह को स्थानीय अस्थायी तुम वापस कब होते हैं, इसलिए कोई भी परिवर्तन करने के लिए कोई आवश्यकता नहीं है अपने कोड। रिटर्न टाइप वह चीज है जो चलती शब्दावली को लागू करने के लिए ज़िम्मेदार है, न कि आप।

आप std::move से का उपयोग करना चाहते हैं स्पष्ट रूप से कुछ ले जाएं, जब यह आपके परीक्षण में सामान्य रूप से नहीं किया जाएगा। (जो ठीक लगता है, क्या वह रिलीज में था? आपको वेक्टर की सामग्री को आउटपुट करना चाहिए, या कंपाइलर इसे अनुकूलित कर देगा।)

यदि आप रैल्यू संदर्भों के बारे में जानना चाहते हैं, read this

+0

परीक्षण डीबग मोड में था। –

+4

@Moo: तब परीक्षण बेकार था। आपको रिलीज में प्रोफाइल करना होगा, और आदर्श रूप से वास्तविक एप्लिकेशन पर। इस मामले में वास्तव में आवश्यकता नहीं है; अर्थशास्त्र को बस जीतें। – GManNickG

+4

मुझे लगता है कि 'वापसी std :: move (local)' प्रति-elision को रोक सकता है, इसलिए मैं इसे 'स्थानीय वापसी' के समान नहीं मानूंगा, लेकिन मुझे इस पर 100% निश्चित नहीं है। – fredoverflow

5

कुछ स्थानांतरित करने का मानक तरीका std::move(x) के साथ है, static_cast नहीं। AFAIK, नामित रिटर्न वैल्यू ऑप्टिमाइज़ेशन वैल्यू को वैल्यू लौटने के साथ लात मारने की संभावना है, इसलिए यह सैमांतिक्स को स्थानांतरित करने से पहले भी अच्छा प्रदर्शन करता।

आपका प्रदर्शन परीक्षण प्रदर्शन के लिए अच्छा अर्थशास्त्र कैसे चल रहा है इसका एक अच्छा उदाहरण है: कॉपी-असाइनमेंट को दस लाख तत्वों की प्रतिलिपि बनाना है, और चाल-असाइनमेंट अनिवार्य रूप से केवल वेक्टर के आंतरिक पॉइंटर्स को स्वैप करता है, जो एक छोटा शब्द है असाइनमेंट या दो, बस कुछ चक्र।

14
return(theVector); 

यह पहले से ही, एक विशेष भाषा नियम की वजह से परोक्ष ले जाता है क्योंकि theVector एक स्थानीय वस्तु है। धारा 12 देखें।8 पैराग्राफ 34 और 35:

जब कुछ मानदंडों को पूरा किया जाता है, एक कार्यान्वयन है, भले ही कॉपी/कदम निर्माता और/या के लिए नाशक एक वर्ग वस्तु की कॉपी/कदम निर्माण न करने का अनुमति दी है वस्तु दुष्प्रभाव है। ऐसे मामलों में, कार्यान्वयन एक ही ऑब्जेक्ट का जिक्र करने के दो अलग-अलग तरीकों के रूप में छोड़े गए प्रतिलिपि/चालन संचालन के स्रोत और लक्ष्य का व्यवहार करता है, और उस ऑब्जेक्ट का विनाश के बाद होता है जब दो ऑब्जेक्ट्स अनुकूलन के बिना नष्ट कर दिया गया होगा। एक वापसी कथन में एक समारोह में एक वर्ग वापसी प्रकार के साथ, -

: नकल के इस इलिजन/ गतिविधियों को स्थानांतरित करने, प्रतिलिपि इलिजन कहा जाता है, निम्नलिखित परिस्थितियों में अनुमति दी है (जो कई प्रतियां खत्म करने को जोड़ा जा सकता है) जब अभिव्यक्ति समारोह वापसी प्रकार, कॉपी/कदम आपरेशन स्वचालित वस्तु समारोह के वापसी मान

में सीधे निर्माण करके छोड़ा जा सकता है के रूप में ही सीवी-अयोग्य प्रकार के साथ एक नॉन-वोलाटाइल स्वत: वस्तु का नाम है

[...]

जब एक प्रतिलिपि आपरेशन के इलिजन के लिए मानदंडों को पूरा कर रहे हैं और वस्तु कॉपी करने के लिए प्रति के लिए निर्माता का चयन करने का lvalue, अधिभार संकल्प द्वारा नामित किया गया है पहले किया जाता है के रूप में अगर वस्तु एक rvalue द्वारा नामित किया गया

ध्यान दें कि आप लौटना चाहिए एक std::vector<T> (मूल्य द्वारा), नहीं एक std::vector<T>&& (संदर्भ द्वारा)।

लेकिन क्यों ब्रांड्स?

return theVector; 
+0

यह ज्यादातर आदत के कारण है, मैंने हमेशा इसे किया है। क्या यह सच में उतना बुरा है? मैं एक तरह से इसे पसंद करता हूँ। के लिए() एक समारोह नहीं है;) (यहां पर लौ-युद्ध शुरू नहीं किया गया था, जो ज्यादातर जीभ-गाल था) –

+6

@Moo: हाँ, लेकिन 'for' * * कोष्ठक की आवश्यकता है। – fredoverflow

7

जीएमएन के उत्तर में जोड़ने के लिए: भले ही आप वापसी प्रकार को std::vector<T> (किसी भी संदर्भ के बिना, अन्यथा आपको यूबी मिलेगा), "1) में वापसी अभिव्यक्ति में आपका परिवर्तन कभी भी बेहतर प्रदर्शन नहीं करेगा, लेकिन यह थोड़ा और खराब हो सकता है। std::vector के रूप में निर्माता के लिए कदम है, और आप एक स्थानीय वस्तु लौटने के लिए, vector की कॉपी निर्माता नहीं बुलाया जाएगा, कोई फर्क नहीं पड़ता कि आप return theVector;, return static_cast<std::vector<T>&&>(theVector);, या return std::move(theVector) लिखा था। पिछले दो मामलों में संकलक को चालक कन्स्ट्रक्टर को कॉल करने के लिए मजबूर किया जाएगा। लेकिन पहले मामले में इस कदम के लिए एनआरवीओ कर सकते हैं, अगर यह पूरी तरह से कदम को अनुकूलित करने की आजादी है। अगर किसी कारण से एनआरवीओ संभव नहीं है, तो केवल संकलक चालक कन्स्ट्रक्टर को कॉल करने का सहारा लेगा। तो return x; से return std::move(x); को x फ़ंक्शन में वापस आने वाले स्थानीय गैर स्थैतिक वस्तु को परिवर्तित न करें, अन्यथा आप संकलक को अन्य अनुकूलन अवसर का उपयोग करने से रोक देंगे।

+1

"जीएमएन के जवाब में जोड़ने के लिए" रोकता है, एक टिप्पणी जोड़ें, न कि एक नया जवाब। – Shoe

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