2013-04-17 12 views
13

यह this topic के लिए कुछ प्रकार का अनुवर्ती है और इसका एक छोटा सा हिस्सा है। पिछले विषय के साथ, मान लीजिए कि हमारे कंपाइलर में constexprstd::initializer_list और std::array के लिए फ़ंक्शन हैं। अब, चलिए सीधे बिंदु पर जाएं।निरंतर अभिव्यक्तियों के बारे में भ्रम

This works:

#include <array> 
#include <initializer_list> 

int main() 
{ 
    constexpr std::array<int, 3> a = {{ 1, 2, 3 }}; 
    constexpr int a0 = a[0]; 
    constexpr int a1 = a[1]; 
    constexpr int a2 = a[2]; 
    constexpr std::initializer_list<int> b = { a0, a1, a2 }; 

    return 0; 
} 

This does not:

#include <array> 
#include <initializer_list> 

int main() 
{ 
    constexpr std::array<int, 3> a = {{ 1, 2, 3 }}; 
    constexpr std::initializer_list<int> b = { a[0], a[1], a[2] }; 

    return 0; 
} 

यह इस त्रुटि के साथ दुर्घटनाओं:

error: 'const std::initializer_list<int>{((const int*)(&<anonymous>)), 3u}' is not a constant expression 

हालांकि मैं constexpr और निरंतर भाव इस बीच के बारे में कुछ कागजात पढ़, इस व्यवहार अभी भी मेरे लिए कोई समझ नहीं आता है। पहला उदाहरण कैसे वैध निरंतर अभिव्यक्ति माना जाता है और दूसरा नहीं? मैं किसी भी स्पष्टीकरण का स्वागत करता हूं ताकि मैं बाद में शांति में आराम कर सकूं।

नोट: मैं सटीक इसे तुरंत, बजना नहीं पहले टुकड़ा संकलन करने के बाद से यह constexpr पुस्तकालय अतिरिक्त उस के लिए सी ++ 14 योजना बनाई गई है को लागू नहीं करता है में सक्षम हो जाएगा होगा। मैंने जीसीसी 4.7 का इस्तेमाल किया।

संपादित करें: ठीक है, यहाँ बड़ा उदाहरण यह जान सकें कि को अस्वीकार कर दिया है और क्या कर रहा है आता नहीं है:

#include <array> 
#include <initializer_list> 

constexpr int foo = 42; 
constexpr int bar() { return foo; } 
struct eggs { int a, b; }; 

int main() 
{ 
    constexpr std::array<int, 3> a = {{ 1, 2, 3 }}; 
    constexpr int a0 = a[0]; 
    constexpr int a1 = a[1]; 
    constexpr int a2 = a[2]; 

    // From Xeo and Andy tests 
    constexpr std::array<int, 1> a = { bar() }; // OK 
    constexpr std::array<int, 3> b = {{ a[0], a[1], a[2] }}; // OK 
    std::initializer_list<int> b = { a[0], a[1], a[2] }; // OK 
    constexpr std::initializer_list<int> b = { a0, a1, a2 }; // OK 
    constexpr std::initializer_list<int> b = { foo }; // OK 
    constexpr std::initializer_list<int> c = { bar() }; // ERROR 
    constexpr std::initializer_list<int> b = { a[0], a[1], a[2] }; // ERROR 

    // From Matheus Izvekov and Daniel Krügler 
    constexpr eggs good = { 1, 2 }; // OK 
    constexpr std::initializer_list<eggs> bad = { { 1, 2 }, { 3, 4 } }; // ERROR 
    constexpr std::initializer_list<eggs> bad2 = { good, good }; // ERROR 

    return 0; 
} 
+0

"जीसीसी में एक बग है" के बारे में कैसे? :) (यह नहीं कह रहा कि यह एक है, बस एक संभावना है।) और वास्तव में, आप अपने स्वयं के अनुरूप लिखकर 'constexpr' जोड़ों के बिना इसका परीक्षण करने में सक्षम होना चाहिए। इसके अलावा, 'constexpr std :: array b = {{a [0], a [1], a [2]}} के बारे में क्या; '? – Xeo

+1

शायद [यह] (http://ideone.com/56iP0Y) समस्या को कम करने में मदद करता है –

+0

@Xeo जो भी मैं सरणी के साथ करता हूं वह ठीक काम करता है (आपके उदाहरण सहित, और एंडी वाले केवल 'std :: arrays' के साथ 'std :: startizer_list' का)। ऐसा लगता है कि समस्या केवल संकलन समय पर 'std :: startizer_list' के साथ होती है। मैंने इसे 'constexpr' या 'std :: array' के बिना पुन: उत्पन्न करने का प्रबंधन नहीं किया था। – Morwenn

उत्तर

1

मैं पता लगा यहाँ क्या हो रहा है:

constexpr std::initializer_list<int> b = { a[0], a[1], a[2] }; 

की a[0]const int& टाइप करें const int प्रकार के अस्थायी रूप से परिवर्तित हो जाता है। फिर g ++ const int* को initializer_list निजी कन्स्ट्रक्टर में पास करने के लिए परिवर्तित करता है। अंतिम चरण में यह अस्थायी का पता लेता है, इसलिए यह निरंतर अभिव्यक्ति नहीं है।

समस्या कॉन्स्ट int में अंतर्निहित रूपांतरण में है। उदाहरण:

constexpr int v = 1; 
const int& r = v; // ok 
constexpr int& r1 = v; // error: invalid initialization of reference of 
         // type ‘int&’ from expression of type ‘const int’ 

वही व्यवहार झगड़ा में है।

मुझे लगता है कि यह रूपांतरण कानूनी है, इसके विपरीत कुछ भी नहीं कहता है।

बारे const int&const int रूपांतरण के लिए, [expr] अनुच्छेद 5:

If an expression initially has the type “reference to T” , the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.

a[0] अभिव्यक्ति का परिणाम है कि मामले में प्रकार const int के अस्थायी XValue है।

constexpr प्रारंभकर्ता में निहित रूपांतरण के बारे में, [dcl.constexpr] अनुच्छेद 9:

... Each implicit conversion used in converting the initializer expressions and each constructor call used for the initialization shall be one of those allowed in a constant expression.

के अस्थायी, [expr पता लेने के बारे में।स्थिरांक] पैरा 2:

...an invocation of a constexpr function with arguments that, when substituted by function invocation substitution, do not produce a constant expression; [ Example:

constexpr const int* addr(const int& ir) { return &ir; } // OK 
static const int x = 5; 
constexpr const int* xp = addr(x); // OK: (const int*)&(const int&)x is an 
            // address contant expression 
constexpr const int* tp = addr(5); // error, initializer for constexpr variable 
            // not a constant expression; 
            // (const int*)&(const int&)5 is not a 
            // constant expression because it takes 
            // the address of a temporary 

— end example ]

+0

निजी सीटीओ आपके द्वारा पास किए गए तत्वों के साथ नहीं कहा जाता है , इसे एक * सरणी * के लिए पॉइंटर कहा जाता है जिसे आपके द्वारा पारित तत्वों से प्रारंभ किया जाता है, और आकार या अंत में पॉइंटर होता है। – Xeo

+0

मैंने वास्तव में [एक बग रिपोर्ट] सबमिट की है (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56991) जीसीसी में। लोगों में से एक ने g ++ को एक और उदाहरण अस्वीकार करने का प्रबंधन किया है जिसमें कोई संदर्भ शामिल नहीं है। – Morwenn

+0

मैं ज्यादातर 'const int' से 'const int' रूपांतरण पर इंगित करता हूं, जो इस तरह के व्यवहार का कारण बनता है और कानूनी लगता है। पहला उदाहरण कच्चे में रूपांतरण दिखाता है। सीटीओआर के बारे में हिस्सा केवल त्रुटि संदेश की व्याख्या करना है। –

1

आपका उदाहरण सभी बीमार बनते हैं।

tl/डॉ: प्रारंभकर्ता गैर निरंतर क्योंकि यह एक अलग अस्थायी हर बार समारोह मूल्यांकन किया जाता है को संदर्भित करता है।

घोषणा:

constexpr std::initializer_list<int> b = { a0, a1, a2 }; 

प्रकार const int [3] (सी ++ 11 [dcl.init.list] पी 5) की एक अस्थायी सरणी बनाता है, फिर बांधता है कि अस्थायी करने std::initializer_list<int> वस्तु के रूप में अगर द्वारा इसके संदर्भ में बाध्यकारी (सी ++ 11 [dcl.init.list] p6)। जब std::initializer_list<int> वस्तु const int [3] अस्थायी को बांधता है

अब, द्वारा सी ++ 11 [expr.const] p4,

For a literal constant expression of array or class type, each subobject [...] shall have been initialized by a constant expression. [...] An address constant expression [...] evaluates to the address of an object with static storage duration.

b के बाद से, स्वत: भंडारण अवधि है, अस्थायी भी स्वत दिया जाता है भंडारण अवधि, इसलिए b का प्रारंभ एक निरंतर अभिव्यक्ति नहीं है क्योंकि यह उस ऑब्जेक्ट के पते को संदर्भित करता है जिसमें स्थिर संग्रहण अवधि नहीं है। तो b की घोषणा खराब गठित है।

क्यों जीसीसी constexprstd::initializer_list वस्तुओं

ऐसे मामलों में जहां प्रारंभकर्ता उपयुक्त रूप से मामूली बात है, जीसीसी के कुछ स्वीकार करता है (और बजना) के बजाय चारों ओर एक नया अस्थायी हर बार बनाने की तुलना में वैश्विक भंडारण के लिए सरणी को बढ़ावा देने के। हालांकि, जीसीसी में, यह कार्यान्वयन तकनीक भाषा अर्थशास्त्र के माध्यम से लीक होती है - जीसीसी सरणी को स्थिर भंडारण अवधि के रूप में मानती है, और प्रारंभिकरण को स्वीकार करती है (या तो सी ++ 11 नियमों के लिए आकस्मिक या जानबूझकर विस्तार)।

वर्कअराउंड (बजना केवल)

आप std::initializer_list<int> देकर अपने उदाहरण मान्य कर सकते हैं वस्तुओं स्थिर भंडारण अवधि:

static constexpr std::initializer_list<int> b = { a0, a1, a2 }; 

बदले में इस सरणी अस्थायी, करने के लिए स्थिर भंडारण अवधि देता है प्रारंभिक अभिव्यक्ति एक निरंतर अभिव्यक्ति है।

बजना और libC++, static जोड़ने के इस ट्वीक पर्याप्त है आपके उदाहरण स्वीकार करने के लिए के लिए (constexpr साथ libc को ++ के <array> और उपयुक्त स्थानों में <initializer_list> जोड़ा) का उपयोग करना।

जीसीसी का उपयोग करना, उदाहरण अभी भी अस्वीकार कर दिया जाता है, इस तरह के रूप निदान के साथ: (स्थिर भंडारण अवधि के साथ)

<stdin>:21:61: error: ‘const std::initializer_list<int>{((const int*)(& _ZGRZ4mainE1c0)), 1u}’ is not a constant expression 

यहाँ, _ZGRZ4mainE1c0 जीवन-विस्तारित सरणी अस्थायी के घायल नाम है, और हम देख सकते हैं कि जीसीसी पूरी तरह से (निजी) initializer_list<int>(const int*, size_t) कन्स्ट्रक्टर को बुला रहा है। मुझे यकीन नहीं है कि क्यों जीसीसी अभी भी इसे अस्वीकार कर रहा है।

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