2013-02-16 39 views
10

सी ++ 11 (जीसीसी 4.7.2 कहें) की वर्तमान स्थिति में, मुझे वैरिएडिक-टेम्पलेट या std::initializer_list का उपयोग करने के बीच कैसे चुनना चाहिए जब मुझे एक ऐसे निर्माता की आवश्यकता हो जो परिवर्तनीय तर्क ले सके?रचनाकारों के लिए, मैं variadic-templates बनाम std :: startizer_list के बीच कैसे चयन करूं?

+3

मुझे यकीन नहीं है (यही कारण है कि यह एक टिप्पणी है) लेकिन क्या प्रारंभिक टेम्पलेट्स सभी प्रकारों को संभाल नहीं सकते हैं, जबकि प्रारंभकर्ता सूची को एक ही प्रकार का होना चाहिए? –

+0

@ जोचिमपिलबोर्ग, बिल्कुल सही, हालांकि आप 'int ...' और 'std :: startizer_list ' के बीच चयन कर सकते हैं। मैं कहता हूं कि जो भी उपयोग करने के लिए और अधिक प्राकृतिक लगता है। – chris

उत्तर

15

एक विविध टेम्पलेट आपको विभिन्न प्रकार के तर्क प्रदान करने की अनुमति देता है, जबकि std::initializer_list तर्क के प्रकार से टेम्पलेट किया गया है। इसका मतलब है कि सूची में सभी तत्वों का प्रकार समान होना चाहिए (या अंतर्निहित प्रकार में परिवर्तनीय होना चाहिए, लेकिन कोई संकुचित रूपांतरण की अनुमति नहीं है)। इस पर निर्भर करता है कि यह आपके लिए वांछनीय है या नहीं, आप एक या दूसरे को चुन सकते हैं।

इसके अलावा, एक variadic टेम्पलेट आमतौर पर डिफ़ॉल्ट विकल्प है यदि आप सही अग्रेषण जरूरत वाक्यात्मक रूप T&& दोनों lvalue संदर्भ और rvalue संदर्भ के लिए बाध्य कर सकते हैं कि में, है, जबकि एक समान प्रकार कटौती initializer_list के लिए नहीं किया जा सकता है:

struct A 
{ 
    // Deduces T& for lvalue references, T for rvalue references, and binds to both 
    template<typename... Ts> 
    A(Ts&&...) { } 

    // This is an rvalue reference to an initializer_list. The above type deduction 
    // does not apply here 
    template<typename T> 
    A(initializer_list<T>&&) { } 
}; 

यह भी ध्यान दें, कि एक निर्माता एक initializer_list को स्वीकार डिफ़ॉल्ट रूप से लागू किया जाएगा जब आप वर्दी प्रारंभ वाक्य रचना (यानी घुंघराले ब्रेसिज़) का उपयोग, भले ही एक और व्यवहार्य निर्माता मौजूद है।

struct A 
{ 
    A(int i) { } 
}; 

struct B 
{ 
    B(int) { } 
    B(std::initializer_list<A>) { } 
}; 

int main() 
{ 
    B b {1}; // Will invoke the constructor accepting initializer_list 
} 
+3

सही अग्रेषण के बारे में थोड़ा विस्तार करना, 'std :: startizer_list :: संदर्भ' (जो परिणाम प्रकार * * l.begin()') का परिणाम है, वह 'टी कॉन्स्ट एंड 'है, जो चाल को प्रतिबंधित करता है। आप केवल-केवल मूल्यों को ही डाल सकते हैं, लेकिन आप उन्हें बाहर नहीं ले जा सकते हैं। –

+0

@ ल्यूकडैंटन: अच्छा बिंदु। –

3

मेरा सुझाव है हमेशा variadic टेम्पलेट्स chosing और std::initializer_list से बचने के लिए जब भी संभव: यह या जो आपके पास है करने की इच्छा नहीं हो सकता है।

यह मैं के साथ std::vector कैसे लागू कर दिया है होता है सी ++ 11:

#include <iostream> 
#include <vector> 

struct exp_sequence { 
    template <typename... T> 
    exp_sequence(T&&...) {} 
}; 

struct from_arglist_t {} from_arglist; 

template <typename T> 
class my_vector { 
    std::vector<T> data; 

public: 
    my_vector(int n, T const& e) : data(n, e) {} 

    template <typename... Args> 
    my_vector(from_arglist_t, Args&&... args) { 
    data.reserve(sizeof...(Args)); 
    exp_sequence{(data.push_back(std::forward<Args>(args)),1)...}; 
    } 

    std::size_t size() { return data.size(); } 
}; 

int main() 
{ 
    std::vector<int> v1{13, 13}; std::cout << v1.size() << '\n'; // 2 
    std::vector<int> v2(13, 13); std::cout << v2.size() << '\n'; // 13 

    my_vector<int> v3{13, 13}; std::cout << v3.size() << '\n'; // 13 
    my_vector<int> v4(13, 13); std::cout << v4.size() << '\n'; // 13 
    my_vector<int> v5(from_arglist, 13, 13); std::cout << v5.size() << '\n'; // 2 
    my_vector<int> v6{from_arglist, 13, 13}; std::cout << v6.size() << '\n'; // 2 
} 

कारण के रूप में main में दिखाया जाता है, सामान्य कोड में initializer_list के प्रयोग पर कोष्ठकों किस प्रकार का था आधार पर अलग अलग व्यवहार हो सकता है चुना। इस तरह के एक कन्स्ट्रक्टर जोड़कर चुपचाप कोड बदलने की संभावना भी है।

//std::vector<move_only> m1{move_only{}}; // won't compile 
my_vector<move_only> m2{from_arglist, move_only{}}; // works fine 
+0

'exp_sequence' चाल साफ है, लेकिन क्या यह वास्तव में सही क्रम में तत्वों को सम्मिलित करने की गारंटी देता है? AFAIR, सी और सी ++ ने कभी भी व्याख्यात्मक क्रम में कार्य तर्कों का मूल्यांकन करने का वादा नहीं किया। इसलिए जब तक भिन्नता टेम्पलेट कन्स्ट्रक्टर के लिए तर्क मूल्यांकन आदेश के बारे में C++ 11 मानक में कोई विशेष गारंटी न हो, तो यह गैर-पोर्टेबल है। – fgp

+1

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

+1

@ipc 'exp_sequence {(data.push_back (std :: आगे (तर्क)) पर वास्तव में क्या चल रहा है, 1) ...}'? हमें 'data.push_back (std :: आगे (तर्क)) '' '' '' '' '1'' से घूमने की आवश्यकता क्यों है? और क्यों '1'? – 0xbadf00d

4
एक variadic टेम्पलेट के साथ

, तर्क की संख्या (sizeof... के माध्यम से और सुलभ) संकलन के दौरान जाना जाता है:

एक अन्य कारण कदम-केवल प्रकार हैं। std::initializer_list के साथ, तर्कों की संख्या केवल रनटाइम पर जानी जाती है। इसलिए निर्णय का हिस्सा इस बात पर निर्भर करता है कि आपको कब चाहिए या जानना है कि आपके पास कितने तर्क हैं।

+0

कंटेनर में संकलन-समय का आकार है, क्योंकि इसे पूर्ण स्थिति में प्रारंभ किया जाना चाहिए (आप push_back, आदि नहीं कर सकते हैं)। यदि आपका मतलब '' आकार() 'विधि का मुद्दा' constexpr' चिह्नित नहीं किया गया है, तो यह C++ 11 में एक दोष था और C++ 14 में तय किया गया था। तो आप कई तरीकों से नामित 'प्रारंभकर्ता_सूची' आदि के आकार पर टेम्पलेट कर सकते हैं, आकार संकलन-समय पर उपलब्ध है। लेकिन यकीन है, शायद विविधता विकल्प के रूप में कई तरीकों से नहीं! –

+2

एक कन्स्ट्रक्टर _parameter_ जो 'startizer_list' है, संकलन के दौरान ज्ञात आकार नहीं है, क्योंकि प्रत्येक साइट पर विभिन्न आकार के प्रारंभकर्ताओं के साथ कन्स्ट्रक्टर को कई साइटों से बुलाया जा सकता है। – KnowItAllWannabe

+0

हाँ, महान बिंदु, मैं पहले पूरी तरह से सोच नहीं रहा था :) –