2011-10-08 23 views
9
#include <initializer_list> 
#include <utility> 

void foo(std::initializer_list<std::pair<int,int>>) {} 
template <class T> void bar(T) {} 

int main() { 
    foo({{0,1}}); //This works 
    foo({{0,1},{1,2}}); //This works 
    bar({{0,1}}); //This warns 
    bar({{0,1},{1,2}}); //This fails 
    bar(std::initializer_list<std::pair<int,int>>({{0,1},{1,2}})); //This works 
} 

यह जीसीसी 4.5.3 में संकलन नहीं है, यह चिह्नित लाइन no matching function for call to ‘bar(<brace-enclosed initializer list>)’ कह के लिए चिह्नित लाइन deducing ‘T’ as ‘std::initializer_list<std::initializer_list<int> >’ कह के लिए एक चेतावनी और एक त्रुटि देता है। जीसीसी पहले बार के प्रकार को बार में क्यों कम कर सकता है, लेकिन दूसरा नहीं, और क्या यह लंबे और बदसूरत कास्टिंग के अलावा इसे ठीक करने का कोई तरीका है?टेम्पलेट्स हमेशा प्रारंभकर्ता सूची प्रकार लगता है कि नहीं है

+0

संभावित डुप्लिकेट [मेरा टेम्पलेट प्रारंभिक सूची क्यों स्वीकार नहीं करता है] (http://stackoverflow.com/questions/4757614/why-doesnt-my-template-accept-an-initializer-list) - वह प्रश्न मूल रूप से एक डुप्लिकेट है। तथ्य यह है कि प्रारंभिक सूची में कटौती टाइप किया जा रहा है एक जी ++ एक्सटेंशन है। – Omnifarious

+0

@Onnifarious यह सुनिश्चित नहीं है कि ये डुप्ली हैं या नहीं। मेरा सवाल स्पष्ट रूप से इस सवाल से कम विवरण मांगना था। –

उत्तर

18

सी ++ 11 के अनुसार जीसीसी bar पर पहले दो कॉल के लिए प्रकार को कम नहीं कर सकता है। यह चेतावनी देता है क्योंकि यह एक्सटेंशन को C++ 11 में लागू करता है।

स्टैंडर्ड का कहना है कि एक समारोह टेम्पलेट के लिए एक कॉल में एक समारोह तर्क एक { ... } है और पैरामीटर नहीं initializer_list<X> (वैकल्पिक रूप से एक संदर्भ पैरामीटर), कि तब पैरामीटर के प्रकार {...} से नहीं लगाया जा सकता है जब। यदि पैरामीटर इस तरह के एक initializer_list<X> है, तो प्रारंभकर्ता सूची के तत्वों X के खिलाफ की तुलना द्वारा स्वतंत्र रूप से निष्कर्ष निकाला जाता है, और तत्वों की कटौती की प्रत्येक मैच के लिए की है।

template<typename T> 
void f(initializer_list<T>); 

int main() { 
    f({1, 2}); // OK 
    f({1, {2}}); // OK 
    f({{1}, {2}}); // NOT OK 
    f({1, 2.0}); // NOT OK 
} 

इस उदाहरण में, पहले ठीक है, और दूसरा ठीक है भी, क्योंकि पहला तत्व पैदावार int टाइप करें, और दूसरा तत्व {2}T के खिलाफ तुलना - इस कटौती एक constradiction उपज नहीं कर सकते हैं के बाद से यह नहीं है कुछ भी कम करें, इसलिए अंततः दूसरी कॉल Tint के रूप में लेती है। तीसरा किसी भी तत्व द्वारा T को घटा नहीं सकता है, इसलिए ठीक नहीं है। अंतिम कॉल दो तत्वों के लिए कटौती के विपरीत विरोधाभास पैदा करता है। बेहतर ब्रेसिज़ आसपास के लोगों (...) को दूर - यह काम करने के लिए

एक तरह से मैं नोट करना चाहिए कि std::initializer_list<U>({...}) कर खतरनाक है

template <class T> void bar(std::initializer_list<std::initializer_list<T>> x) { 
    // ... 
} 

पैरामीटर प्रकार के रूप में इस तरह के एक प्रकार का उपयोग करने के लिए है। आपके मामले में यह दुर्घटना से काम करते हैं, लेकिन पर विचार

std::initializer_list<int> v({1, 2, 3}); 
// oops, now 'v' contains dangling pointers - the backing data array is dead! 

कारण यह है कि ({1, 2, 3}) यह एक अस्थायी initializer_list<int>{1, 2, 3} के साथ जुड़े गुजर initializer_list<int> की कॉपी/कदम निर्माता कहता है होता है। तब प्रारंभिक वस्तु समाप्त हो जाएगी और प्रारंभ होने पर मर जाएगा। जब कि अस्थायी उद्देश्य यह है कि सूची के साथ जुड़ा हुआ है मर जाता है, समर्थन-अप सरणी डेटा पकड़े भी नष्ट कर दिया जाएगा (अगर इस कदम elided है, यह के रूप में "वी" जब तक रहना होगा, कि बुरा है, क्योंकि यह भी व्यवहार नहीं होता खराब गारंटी!)। कोष्ठक को छोड़ते हुए रखकर v सीधे सूची के साथ जुड़ा हुआ है, और समर्थन सरणी डेटा नष्ट हो जाता है केवल जब v नष्ट हो जाता है।

+0

मुझे अंतिम पैराग्राफ नहीं समझा। क्या आप उस पर थोड़ा और विस्तार कर सकते हैं? मुझे 'std :: startizer_list v ({1, 2, 3}) के बीच कोई अंतर नहीं दिख रहा है;' और 'std :: startizer_list v {1, 2, 3};'। वे मेरे लिए समान दिखते हैं। – Nawaz

+0

@ नवाज मैंने सोचा कि मैंने उस पर विस्तार किया है जो मैं कर सकता हूं। यदि आपके पास अभी भी प्रश्न हैं, तो मैं एक नया प्रश्न खोलने की सलाह देता हूं, क्योंकि आवश्यक विस्तार एक नया उत्तर वारंट करेगा। –

+0

@ नवाज ने आखिरी अनुच्छेद के लिए एक नया प्रश्न खोल दिया था? आप टिप्पणियों में इसे यहां अग्रेषित कर सकते हैं। – SebNag

4

वर्दी प्रारंभ जानते हुए भी किस प्रकार प्रारंभ किया जा रहा है पर निर्भर करता है। {1} कई चीजों का मतलब हो सकता है। एक int के लिए आवेदन किया है, तो यह एक 1. से भर जाता है एक std::vector<int> के लिए आवेदन किया है, यह पहला तत्व में 1 साथ, एक एक तत्व vector बनाने के लिए मायने रखता है। और इसी तरह।

जब आप खाका समारोह जो प्रकार है फोन पूरी तरह से स्वेच्छापूर्ण है तो वर्दी आरंभीकरण के साथ काम करने के लिए कोई प्रकार की जानकारी है। और बिना किसी प्रकार की जानकारी, वर्दी प्रारंभिक काम नहीं कर सकता है।

उदाहरण के लिए:

bar({{0,1}}); 

आपके पास इस प्रकार std::initializer_list<std::pair<int,int>> के होने की उम्मीद। लेकिन संकलक को यह कैसे पता चलेगा? bar का पहला पैरामीटर एक अनियंत्रित टेम्पलेट है; यह सचमुच कोई प्रकार हो सकता है। गाय संकलक संभवतः अनुमान लगा सकता है कि आप इस विशिष्ट प्रकार का मतलब है?

काफी आसानी से, यह नहीं हो सकता है। कंपाइलर्स अच्छे हैं, लेकिन वे क्लेयरवोयंट नहीं हैं। समान प्रारंभिकता केवल प्रकार की जानकारी की उपस्थिति में काम कर सकती है, और अनियंत्रित टेम्पलेट्स उन सभी को हटा दें।

सभी अधिकारों से, सी ++ 11 के अनुसार, उस पंक्ति को संकलित करने में विफल होना चाहिए था। यह {...} होने का इरादा किस प्रकार से कम नहीं कर सकता है, इसलिए यह असफल होना चाहिए था। यह एक जीसीसी बग या कुछ की तरह दिखता है।

+0

के बारे में गलत हूं, तो मैं इसे देखूंगा और अपना जवाब बढ़ा दूंगा, यह काम नहीं करेगा क्योंकि संकलक को यह नहीं पता कि यह 'टी' को कैसे कम करेगा, अगर इसमें केवल' std :: pair है 'और' {1, 2} '। मुझे लगता है कि आप अपने उत्तर के पहले भाग में समस्या का वर्णन कैसे करते हैं। 'जोड़ी ' :) –

+0

@ JohannesSchaub-litb के लिए 'टी' कैसे प्राप्त करें के लिए एक ही तर्क लागू होता है: पर्याप्त मेला। हटा दिया। –

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