2012-03-02 15 views
9

मैं समझता हूँ कि निम्नलिखित की तरह कुछ कर रही है कि:रोकें अभिव्यक्ति rvalue के लिए बाध्य टेम्पलेट्स का संदर्भ

auto&& x = Matrix1() + Matrix2() + Matrix3(); 
std::cout << x(2,3) << std::endl; 

एक मूक रनटाइम त्रुटि का कारण होगा यदि मैट्रिक्स आपरेशन (जैसे boost::ublas के रूप में) अभिव्यक्ति टेम्पलेट का उपयोग।

क्या संकलक टेम्पलेट को डिज़ाइन करने का कोई तरीका है ताकि संकलक को ऐसे कोड को संकलित करने से रोका जा सके जिसके परिणामस्वरूप रनटाइम पर समय-समय पर समाप्त होने वाले अस्थायी उपयोग हो सकते हैं?

+3

यदि आप इस तरह के बाध्यकारी को मना करते हैं, तो 'ऑपरेटर + (अभिव्यक्ति_टम्प्लेट कॉन्स और, अभिव्यक्ति_टैम्पप्लेट कॉन्स एंड)' संकलित नहीं होगा। –

+0

@ आर। मार्टिनिन्हो फर्नांडीस: 'expression_template const &' द्वारा 'ऑपरेटर +' को अपने तर्क क्यों लेना चाहिए? मैं कल्पना कर सकता हूं कि 'ऑपरेटर +' किसी प्रकार की प्रॉक्सी के माध्यम से अपने तर्क ले सकता है जो अभी भी 'कॉन्स्ट रेफरेंस' को अभिव्यक्ति टेम्पलेट्स के लिए असुरक्षित रूप से बाध्य नहीं करेगा। (मैं यह नहीं कह रहा कि यह संभव है, लेकिन कम से कम यह असंभव नहीं है)। – Mankarse

+0

@Mankarse आप निहित रूपांतरण और टेम्पलेट प्रकार की कटौती को मिश्रित नहीं कर सकते हैं। चूंकि आपको 'ऑपरेटर +' के लिए काम करने के लिए टाइप कटौती चुननी है, इसलिए इसके लिए तर्क अभिव्यक्ति टेम्पलेट का प्रकार होना चाहिए। (जब तक कि मैं "कुछ प्रकार की प्रॉक्सी" से आपका मतलब समझ नहीं पा रहा हूं) –

उत्तर

7

है वहाँ अभिव्यक्ति टेम्पलेट्स को डिजाइन करने में इस तरह के कोड है कि के उपयोग में हो सकता है संकलन से संकलक को रोकने के लिए की किसी भी तरह से (मैं असफल इस समस्या के समाधान करने का प्रयास किया, प्रयास here है) रनटाइम पर समाप्त हो चुके अस्थायी?

नहीं। यह वास्तव में सी ++ 11 के अंतिम मानकीकरण से पहले पहचाना गया था, लेकिन मुझे नहीं पता कि इसे कभी समिति के नोटिस में लाया गया था या नहीं। ऐसा नहीं है कि एक फिक्स आसान हो गया होगा। मुझे लगता है कि सबसे आसान चीज उन प्रकारों पर एक झंडा होगी जो auto इसे कम करने की कोशिश करते हैं, लेकिन यह जटिल भी होगा क्योंकि decltype इसे भी कम कर सकता है, साथ ही टेम्पलेट तर्क कटौती भी कर सकता है। और इन तीनों को एक ही तरीके से परिभाषित किया गया है, लेकिन शायद आप नहीं चाहते कि उत्तरार्द्ध विफल हो।

बस अपनी लाइब्रेरी को उचित रूप से दस्तावेज करें और उम्मीद करें कि कोई भी उन्हें इस तरह कैप्चर करने का प्रयास नहीं करता है।

+0

क्या static_cast (x) को वैध होने से रोकना है यदि x एक 'T &' है? मैं पूछता हूं क्योंकि नामित अस्थायी 'टी एंड' बनते हैं जब बाद में उपयोग किया जाता है, अगर उन्हें 'std :: move'd' से रोक दिया जा सकता है या अन्यथा 'टी एंड' में परिवर्तित किया जा सकता है तो छेद बंद किया जा सकता है। – Clinton

+1

नामित अस्थायी एल-वैल्यू हैं, इसलिए उन्हें एल-वैल्यू संदर्भ बनना चाहिए। और यदि 'static_cast ' सभी 'T &' के लिए मान्य नहीं था, तो अग्रेषण और आगे बढ़ना विफल हो जाएगा। तो नहीं, अग्रेषण और आगे बढ़ने का कोई रास्ता नहीं है। दोबारा, आपको अपने कोड को तोड़ने के लिए उपयोगकर्ताओं पर भरोसा करना होगा। या अभिव्यक्ति टेम्पलेट्स का उपयोग न करें। –

1

जैसा कि मैं समझता हूं, आपकी समस्या का मूल यह है कि अभिव्यक्ति टेम्पलेट अस्थायी में कुछ अन्य अस्थायी लोगों के संदर्भ/पॉइंटर्स हो सकते हैं। और ऑटो & & का उपयोग करके हम केवल अभिव्यक्ति टेम्पलेट का जीवन अस्थायी रूप से बढ़ाते हैं, लेकिन अस्थायी जीवनकाल का संदर्भ नहीं है। क्या यह सही है?

उदाहरण के लिए, this आपके मामले में है?

#include <iostream> 
#include <deque> 
#include <algorithm> 
#include <utility> 
#include <memory> 
using namespace std; 

deque<bool> pool; 

class ExpressionTemp; 
class Scalar 
{ 
    bool *alive; 

    friend class ExpressionTemp; 

    Scalar(const Scalar&); 
    Scalar &operator=(const Scalar&); 
    Scalar &operator=(Scalar&&); 
public: 
    Scalar() 
    { 
     pool.push_back(true); 
     alive=&pool.back(); 
    } 
    Scalar(Scalar &&rhs) 
     : alive(0) 
    { 
     swap(alive,rhs.alive); 
    } 
    ~Scalar() 
    { 
     if(alive) 
      (*alive)=false; 
    } 
}; 
class ExpressionTemp 
{ 
    bool *operand_alive; 
public: 
    ExpressionTemp(const Scalar &s) 
     : operand_alive(s.alive) 
    { 
    } 
    void do_job() 
    { 
     if(*operand_alive) 
      cout << "captured operand is alive" << endl; 
     else 
      cout << "captured operand is DEAD!" << endl; 
    } 
}; 

ExpressionTemp expression(const Scalar &s) 
{ 
    return {s}; 
} 
int main() 
{ 
    { 
     expression(Scalar()).do_job(); // OK 
    } 
    { 
     Scalar lv; 
     auto &&rvref=expression(lv); 
     rvref.do_job(); // OK, lv is still alive 
    } 
    { 
     auto &&rvref=expression(Scalar()); 
     rvref.do_job(); // referencing to dead temporary 
    } 
    return 0; 
} 

यदि हाँ तो संभव समाधान में से एक, अभिव्यक्ति टेम्पलेट temporaries जो temporaries से चले गए संसाधनों पकड़ के विशेष प्रकार का बनाना है।

उदाहरण के लिए, this दृष्टिकोण (आप फिर से बग केस प्राप्त करने के लिए BUG_CASE मैक्रो परिभाषित कर सकते हैं) की जांच करें।

//#define BUG_CASE 

#include <iostream> 
#include <deque> 
#include <algorithm> 
#include <utility> 
#include <memory> 
using namespace std; 

deque<bool> pool; 

class ExpressionTemp; 
class Scalar 
{ 
    bool *alive; 

    friend class ExpressionTemp; 

    Scalar(const Scalar&); 
    Scalar &operator=(const Scalar&); 
    Scalar &operator=(Scalar&&); 
public: 
    Scalar() 
    { 
     pool.push_back(true); 
     alive=&pool.back(); 
    } 
    Scalar(Scalar &&rhs) 
     : alive(0) 
    { 
     swap(alive,rhs.alive); 
    } 
    ~Scalar() 
    { 
     if(alive) 
      (*alive)=false; 
    } 
}; 
class ExpressionTemp 
{ 
#ifndef BUG_CASE 
    unique_ptr<Scalar> resource; // can be in separate type 
#endif 
    bool *operand_alive; 
public: 
    ExpressionTemp(const Scalar &s) 
     : operand_alive(s.alive) 
    { 
    } 
#ifndef BUG_CASE 
    ExpressionTemp(Scalar &&s) 
     : resource(new Scalar(move(s))), operand_alive(resource->alive) 
    { 
    } 
#endif 
    void do_job() 
    { 
     if(*operand_alive) 
      cout << "captured operand is alive" << endl; 
     else 
      cout << "captured operand is DEAD!" << endl; 
    } 
}; 

template<typename T> 
ExpressionTemp expression(T &&s) 
{ 
    return {forward<T>(s)}; 
} 
int main() 
{ 
    { 
     expression(Scalar()).do_job(); // OK, Scalar is moved to temporary 
    } 
    { 
     Scalar lv; 
     auto &&rvref=expression(lv); 
     rvref.do_job(); // OK, lv is still alive 
    } 
    { 
     auto &&rvref=expression(Scalar()); 
     rvref.do_job(); // OK, Scalar is moved into rvref 
    } 
    return 0; 
} 

आपका ऑपरेटर/समारोह भार के different types वापस आ सकते हैं, टी पर & &/स्थिरांक टी & तर्कों के आधार:

#include <iostream> 
#include <ostream> 
using namespace std; 

int test(int&&) 
{ 
    return 1; 
} 
double test(const int&) 
{ 
    return 2.5; 
}; 

int main() 
{ 
    int t; 
    cout << test(t) << endl; 
    cout << test(0) << endl; 
    return 0; 
} 

तो, आपकी अभिव्यक्ति टेम्पलेट अस्थायी नहीं है जब संसाधनों temporaries से ले जाया गया है - यह है आकार प्रभावित नहीं होगा।

+0

तकनीकी रूप से, 'ऑटो' * समस्या की जड़ है। आप आमतौर पर 'निजी' सदस्यों के पीछे अभिव्यक्ति टेम्पलेट प्रकार को छुपा सकते हैं। यह "वर्तनी के लिए मुश्किल" नहीं है; संकलक * आपको स्पष्ट रूप से प्रकार का उपयोग करने से रोक देगा *। समस्या यह है कि 'ऑटो' और 'decltype 'पूरे' सार्वजनिक/निजी 'चीज़ को साइड-चरण, जिससे आप अन्य प्रकार के प्रकार बनाने की अनुमति दे सकते हैं, जब तक कि आप वास्तव में कभी भी नाम का उपयोग नहीं करते। –

+0

ठीक है, मैं देखता हूं - ऑटो ने "निजी" सुरक्षा की परत तोड़ दी थी, जिसने अधिक मौलिक मुद्दे की रक्षा की थी। लेकिन उदाहरण में पूछे जाने वाले प्रश्न - http://ideone.com/7i3yT, auto && को ExpressionTemplate && के साथ प्रतिस्थापित किया जा सकता है। –

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