2017-12-26 104 views
16

का __constructible विशेषता C++ 17 पर स्विच करते समय मानकसमाधान को प्रतिस्थापित करते समय क्लैंग 5 का वास्तव में अजीब और अप्रत्याशित व्यवहार पता चला था। कुछ कारणों से, emplace() पैरामीटर वर्ग की std::is_constructible विशेषता के दोषपूर्ण मूल्यांकन के कारण अक्षम किया जा रहा था।क्लैंग 5: std :: वैकल्पिक तात्कालिकता शिकंजा std :: पैरामीटर प्रकार

कुछ विशिष्ट पूर्व शर्त से पहले ही reproduces संतुष्ट किया जाना चाहिए:

#include <optional> 

/// Precondition #1: T must be a nested struct 
struct Foo 
{ 
    struct Victim 
    { 
     /// Precondition #2: T must have an aggregate-initializer 
     /// for one of its members 
     std::size_t value{0}; 
    }; 

    /// Precondition #3: std::optional<T> must be instantiated in this scope 
    std::optional<Victim> victim; 

    bool foo() 
    { 
     std::optional<Victim> foo; 

     // An error 
     foo.emplace(); 
     /// Assertion is failed 
     static_assert(std::is_constructible<Victim>::value); 
    } 
}; 

लाइव उदाहरण पर godbolt.org


बदलें किसी भी पूर्व शर्त की और उम्मीद के रूप में यह संकलित करता है। क्या मानक में कुछ अज्ञात असंगतता है जो अनुपालन करते समय इस कोड को अस्वीकार कर देता है?

एक तरफ ध्यान दें के रूप में: जीसीसी 7.1 और जीसीसी 7.2 ऊपर कोड के साथ कोई समस्या नहीं है।


बग रिपोर्ट पर: bugs.llvm.org

+0

बहुत अच्छी तरह से एक संकलक बग हो सकता है। –

+0

@ क्रिसलुएंगो, मुझे उम्मीद है, क्योंकि मानक से ठीक करना आसान है। – GreenScape

+1

इसके मूल में, आपका एक भाषा वकील प्रश्न है, वास्तव में। इसका उत्तर दिया जाना चाहिए। – StoryTeller

उत्तर

7

यह एक संकलक बग की तरह दिखता है। से [class]

एक वर्ग वर्ग विनिर्देशक के समापन } में एक पूरी तरह से परिभाषित ऑब्जेक्ट प्रकार (या पूरा प्रकार) माना जाता है।

जिसका मतलब है Victimstd::optional<Victim> पर पूरा हो गया है, यह इस संदर्भ में किसी भी अन्य प्रकार से अलग नहीं बना रही है।

से [meta]

एक टेम्पलेट विशेषज्ञता is_­constructible<T, Args...> के लिए विधेय हालत संतुष्ट यदि और केवल यदि निम्न चर परिभाषा हैं कुछ के लिए अच्छी तरह से गठन किया जा होगा आविष्कार चर t: T t(declval<Args>()...);

Args... के तर्कों के साथ t को प्रत्यक्ष-आरंभ करने वाला है, या sizeof...(Args) == 0, तो यह मूल्य-प्रारंभिकहै 0।

इस मामले में, value-initializing t is to default-initializet, जो मान्य है std::is_constructible_v<Victim> सत्य होना चाहिए।

उन सभी के साथ, कंपाइलर्स struggling a lot संकलित प्रतीत होता है।

+1

उसी पैराग्राफ से: "कक्षा के सदस्य-विनिर्देश के भीतर, वर्ग को [...] डिफ़ॉल्ट सदस्य प्रारंभकर्ताओं (नेस्टेड कक्षाओं में ऐसी चीजों सहित) के रूप में पूर्ण माना जाता है।" यह सूचित करते हैं कि compilers डिफ़ॉल्ट सदस्य initializers संसाधित नहीं कर सकता, जब तक वे समापन 'देख}' (और नेस्टेड कक्षाओं के लिए, जब तक वे समापन '}' संलग्नित क्लास के देखें) लगता है। – cpplearner

+2

@cpplearner मेरा मानना ​​है कि इस तथ्य को संदर्भित करता है कि (संलग्न) वर्ग अपने शरीर के भीतर अपूर्ण है, न कि इसकी घोंसला वाली कक्षा अधूरा –

+1

मेरा मुद्दा है, डिफ़ॉल्ट सदस्य प्रारंभकर्ता अभी भी टोकन सूप हैं (यानी।वे पार्स नहीं किए गए हैं) संलग्न वर्ग के भीतर, और कुल आरंभ करने के लिए इन डिफ़ॉल्ट सदस्य प्रारंभकर्ताओं को संदर्भित करने की आवश्यकता होगी। यह SFINAE संदर्भ के बाहर अधिक स्पष्ट है: 'संरचना बाहरी {संरचना आंतरिक {int x = 4; }; decltype (आंतरिक()) ए; }; ' – cpplearner

3

ठीक है, प्रासंगिक उद्धरण खोद गया। इस मामले का क्रूक्स std::is_constructible को Victim को संभालना चाहिए। सबसे निर्णायक प्राधिकरण सी ++ 17 (एन 465 9) है।सबसे पहले [meta.unary.prop/8]:

एक टेम्पलेट विशेषज्ञता के लिए विधेय हालत is_­constructible<T, Args...> संतुष्ट यदि और केवल यदि चर परिभाषा निम्नलिखित हैं कुछ के लिए अच्छी तरह से गठन किया जा होगा चर टी का आविष्कार:

T t(declval<Args>()...); 

[ नोट: ये टोकन एक समारोह घोषणा के रूप में कभी नहीं समझा जाता है। - अंत टिप्पणी] पहुंच जाँच एक संदर्भ टी और आर्ग के किसी भी के लिए असंबंधित में के रूप में अगर किया जाता है। वैरिएबल प्रारंभिकरण के तत्काल संदर्भ की केवल वैधता माना जाता है।

टिप्पणी मैं प्रकाश डाला (एक नोट होने के कारण) प्रामाणिक नहीं है, लेकिन यह [temp.variadic]/7 साथ मेल खाता है:

... जब एन शून्य है, विस्तार की इन्स्टेन्शियशन एक का उत्पादन खाली सूची इस तरह के एक इन्स्टेन्शियशन संलग्न निर्माण की वाक्यात्मक व्याख्या में परिवर्तन नहीं होता है, यहां तक ​​कि उन मामलों में जहां सूची पूरी तरह छोड़ते हुए में होगा अन्यथा बीमार का गठन किया जा या व्याकरण में एक अस्पष्टता में परिणाम होगा।

तो is_­constructible के प्रयोजनों के लिए, इस T t(); वास्तव में t एक चर घोषणा करता है। यह प्रारंभ मूल्य प्रारंभ है, क्योंकि [dcl.init/11] कहते हैं जितना:

एक वस्तु जिसका प्रारंभकर्ता कोष्ठकों के एक खाली सेट है, जैसे कि,(), मूल्य प्रारंभ किया जाएगा।

इसका मतलब है कि विशेषता जांचने के बाद Victim मूल्य-प्रारंभिक हो सकती है। यह कौन सा हो सकता है यह एक कुल है, लेकिन एक निहित रूप से डिफ़ॉल्ट डिफ़ॉल्ट ctor अभी भी संकलक द्वारा परिभाषित किया गया है (स्पष्ट रूप से मूल्य प्रारंभिक समर्थन का समर्थन करने के लिए)।

लंबी कहानी कम। क्लैंग में एक बग है, आपको इसकी रिपोर्ट करनी चाहिए।

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