2016-07-05 14 views
7

का व्यवहार बदलता है मैंने header for optionally-lazy parameters बनाया है (GitHub repository में भी दिखाई देता है)। (यह not my first question based on the header है।)वर्चुअल विनाशक decltype

मेरे पास बेस-क्लास टेम्पलेट और दो व्युत्पन्न-श्रेणी टेम्पलेट्स हैं। बेस-क्लास टेम्पलेट में के साथ protected कन्स्ट्रक्टर है। इस कन्स्ट्रक्टर को केवल एक विशेष व्युत्पन्न-वर्ग द्वारा बुलाया जाता है। static_assert के अंदर मैं decltype का उपयोग कर रहा हूं।

वास्तव में विचित्र बात यह है कि एक नाम के प्रकार decltype अंदर किसी भी तरह से प्रभावित होता है या नहीं, मेरा आधार स्तरीय टेम्पलेट में एक आभासी नाशक होती है।

#include <type_traits> 
#include <utility> 

template <typename T> 
class Base 
{ 
    protected: 
    template <typename U> 
    Base(U&& callable) 
    { 
     static_assert(
      std::is_same< 
       typename std::remove_reference<decltype(callable())>::type, T 
      >::value, 
      "Expression does not evaluate to correct type!"); 
    } 

    public: 
    virtual ~Base(void) =default; // Causes error 

    virtual operator T(void) =0; 
}; 

template <typename T, typename U> 
class Derived : public Base<T> 
{ 
    public: 
    Derived(U&& callable) : Base<T>{std::forward<U>(callable)} {} 

    operator T(void) override final 
    { 
     return {}; 
    } 
}; 

void TakesWrappedInt(Base<int>&&) {} 

template <typename U> 
auto MakeLazyInt(U&& callable) 
{ 
    return Derived< 
      typename std::remove_reference<decltype(callable())>::type, U>{ 
     std::forward<U>(callable)}; 
} 

int main() 
{ 
    TakesWrappedInt(MakeLazyInt([&](){return 3;})); 
} 

ध्यान दें कि यदि नाशक बाहर टिप्पणी की है, तो यह त्रुटि के बिना संकलित:

यहाँ मेरी MCVE है।

आशय callable प्रकार U की अभिव्यक्ति है कि, जब () ऑपरेटर के साथ कहा जाता है, प्रकार T के बारे में कुछ रिटर्न होने के लिए है। Base में आभासी विनाशक के बिना, ऐसा प्रतीत होता है कि इसका मूल्यांकन सही ढंग से किया जाता है; आभासी विनाशक के साथ, ऐसा लगता है कि callabele का प्रकार Base<T> है (जहां तक ​​मैं कह सकता हूं, कोई समझ नहीं आता है)।

recursive_lazy.cpp:13:55: error: type 'Base<int>' does not provide a call operator 
       typename std::remove_reference<decltype(callable())>::type, T 
                 ^~~~~~~~ 
recursive_lazy.cpp:25:7: note: in instantiation of function template specialization 
     'Base<int>::Base<Base<int> >' requested here 
class Derived : public Base<T> 
    ^
1 error generated. 

Here is an online version.

संपादित करें:=delete कॉपी-निर्माता आईएनजी

recursive_lazy.cpp: In instantiation of ‘Base<T>::Base(U&&) [with U = Base<int>; T = int]’: 
recursive_lazy.cpp:25:7: required from ‘auto MakeLazyInt(U&&) [with U = main()::<lambda()>]’ 
recursive_lazy.cpp:48:47: required from here 
recursive_lazy.cpp:13:63: error: no match for call to ‘(Base<int>)()’ 
       typename std::remove_reference<decltype(callable())>::type, T 

यहाँ बजना ++ 3.7 के त्रुटि संदेश है:

यहाँ जी ++ 5.1 के त्रुटि संदेश नहीं है भी इस त्रुटि को ट्रिगर करता है।

+2

मैं तुम्हें अजीब आभासी नाशक त्रुटि के बारे में नहीं बता सकता, लेकिन मुझे लगता है कि 'व्युत्पन्न (यू && प्रतिदेय)' एक आर मूल्य लेता है संदर्भ और सार्वभौमिक संदर्भ नहीं। क्या इसका इरादा है? – SirGuy

+0

क्या आप उदाहरण के लिए कुछ आउटपुट जोड़ सकते हैं जो दिखा रहा है कि यह क्या करना है। अंतिम ऑपरेटर टी(); के कारण, TakesWrappedInt को आपके उदाहरण के साथ शून्य नहीं लगता है। –

+0

@GuyGreer नहीं, इसका इरादा नहीं है। ऐसा इसलिए है क्योंकि टेम्पलेट विशिष्ट होने के बाद, यू अब टेम्पलेट प्रकार नहीं है? –

उत्तर

10

समस्या यह है कि जब आप नाशक घोषित, निहित चाल निर्माता घोषित नहीं किया जाएगा क्योंकि

(N4594 12,8/9)

एक दसवीं कक्षा की परिभाषा को स्पष्ट रूप से घोषित नहीं करता है एक चाल निर्माता, एक गैर स्पष्ट एक के रूप में चूक परोक्ष घोषित हो सकता है अगर और

केवल तभी ...

    ,210
  • एक्स एक उपयोगकर्ता के घोषित नाशक नहीं है

Base उपयोगकर्ता के घोषित नाशक (यह कोई फर्क नहीं पड़ता कि यह डिफॉल्ट की है) है।

जब MakeLazyIntDerived ऑब्जेक्ट का निर्माण करने की कोशिश करता है, तो यह Derived चालक को स्थानांतरित करता है।

Derived परोक्ष-घोषित चाल निर्माता फोन नहीं करता है Base चाल निर्माता (क्योंकि जो मौजूद नहीं है), बल्कि अपने टेम्प्लेटेड Base(U&&) निर्माता।

और यहाँ समस्या है, callable पैरामीटर प्रतिदेय वस्तु लेकिन Base वस्तु है, जो वास्तव में operator() शामिल नहीं है शामिल नहीं है।

हल करने के लिए समस्या बस Base अंदर कदम निर्माता की घोषणा:

template <typename T> 
class Base 
{ 
    protected: 
    template <typename U> 
    Base(U&& callable) 
    { 
     static_assert(
      std::is_same< 
       typename std::remove_reference<decltype(callable())>::type, T 
      >::value, 
      "Expression does not evaluate to correct type!"); 
    } 

    public: 
    virtual ~Base(void) =default; // When declared, no implicitly-declared move constructor is created 

    Base(Base&&){} //so we defined it ourselves 

    virtual operator T(void) =0; 
}; 
+1

... और यह कॉपी को हटाने के बारे में ओपी के अपडेट को भी समझाता है निर्माता ... – davidbak

+0

अहह्ह। मैं उस नियम को जानता हूं लेकिन त्रुटि संदेश के आधार पर यह नहीं देखा कि यह कैसे लागू होता है। धन्यवाद। क्या आपको पता है कि सी ++ 17 की गारंटीकृत प्रति-एलीशन इस समस्या से बचना होगा? –

+0

@KyleStrand अच्छी तरह से दूसरे नियम पर आधारित * फ़ंक्शन कॉल में, यदि रिटर्न स्टेटमेंट का ऑपरेशन एक प्रबल होता है और फ़ंक्शन का रिटर्न प्रकार उस प्रकृति के प्रकार जैसा ही होता है। *, मुझे लगता है कि कंपाइलर कन्स्ट्रक्टर को बढ़ाएगा , लेकिन मुझे यकीन नहीं है कि इस मामले में संकलक ने इसे क्यों नहीं बढ़ाया। – PcAF

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