2010-09-13 4 views
31

मैं एक फ़ंक्शन (संभवतः सदस्य फ़ंक्शन नहीं बना रहा हूं, यह महत्वपूर्ण नहीं है ... शायद यह करता है?) जिन्हें अज्ञात संख्या में तर्क स्वीकार करने की आवश्यकता है, लेकिन मैं चाहता हूं कि वे सभी एक ही प्रकार के हों। मुझे पता है कि मैं एक सरणी या वेक्टर में गुजर सकता हूं, लेकिन मैं अतिरिक्त संरचना या यहां तक ​​कि अतिरिक्त ब्रैकेट के बिना सीधे तर्कों की सूची स्वीकार करने में सक्षम होना चाहता हूं। यह खुद के द्वारा विविधतापूर्ण कार्यों की तरह दिखता नहीं है, और मुझे यकीन नहीं था कि इस w/variadic टेम्पलेट फ़ंक्शंस के बारे में कैसे जाना है। यहाँ है कि मैं क्या (, lol सही होने की संभावना नहीं कोड की तुलना में अधिक है, और पूरी तरह से नहीं ड्रेगन की सूची प्राप्त करने के प्रयोजन के लिए) के लिए लक्ष्य कर रहा हूँ अनिवार्य रूप से बताया गया है:सरणी, वेक्टर, structs, आदि का उपयोग कर variadic समारोह या variadic टेम्पलेट समारोह w/बाहर पारित सभी तर्कों के लिए एक प्रकार निर्दिष्ट?

//typedef for dragon_list_t up here somewhere. 

enum Maiden { 
    Eunice 
    , Beatrice 
    , Una_Brow 
    , Helga 
    , Aida 
}; 

dragon_list_t make_dragon_list(Maiden...) { 
    //here be dragons 
} 

या

template<Maiden... Maidens> dragon_list_t make_dragon_list(Maidens...) { 
    //here be dragons 
} 

उपयोग

dragon_list_t dragons_to_slay 
    = make_dragon_list(Maiden.Eunice, Maiden.Helga, Maiden.Aida) 
; 

उपरोक्त पहले से ही कुछ चीजों की कोशिश की, कोई पासा नहीं। सुझाव? स्पष्ट oversights मैंने किया हो सकता है? मैं जानता हूँ कि यह बजाय यह करने के लिए एक बहुत बड़ा सौदा नहीं हो सकता है:

dragon_list_t make_dragon_list(std::array<Maiden> maidens) { 
    //here be dragons. 
} 
dragon_list_t dragons_to_slay 
    = make_dragon_list({Maiden.Eunice, Maiden.Helga, Maiden.Aida}) 
; 

लेकिन मैं बहुत बल्कि यदि संभव हो तो यह पहली बार जिस तरह से ऐसा करने में सक्षम होगा।

उत्तर

21

आप केवल विविध टेम्पलेट द्वारा तर्क स्वीकार कर सकते हैं और टाइपशेकिंग को परिवर्तित होने पर बाद में वैधता की जांच कर सकते हैं।

आप समारोह इंटरफ़ेस स्तर पर परिवर्तनीयता की जांच कर सकते हालांकि, उदाहरण के लिए एकमुश्त गलत तर्क को खारिज के लिए अधिभार संकल्प का उपयोग करने के लिए, अपने यूज-केस के लिए SFINAE

template<typename R, typename...> struct fst { typedef R type; }; 

template<typename ...Args> 
typename fst<void, 
    typename enable_if< 
    is_convertible<Args, ToType>::value 
    >::type... 
>::type 
f(Args...); 

का उपयोग करके यदि आप करने के लिए कदम पता एक std::array<> से करने के लिए अपने dragon_list_t तो आप पहले से ही यह ऊपर पहले विकल्प के अनुसार हालांकि समाधान कर लिया है जाना ("कन्वर्ट-बाद में"):

template<typename ...Items> 
dragon_list_t make_dragon_list(Items... maidens) { 
    std::array<Maiden, sizeof...(Items)> arr = {{ maidens ... }}; 
    // here be dragons 
} 

आप ऊपरके साथ इस जोड़ देते हैं तोदृष्टिकोण आपके पास एक अस्वीकार-प्रारंभिक टेम्पलेट है जो तर्कों पर ओवरलोड रिज़ॉल्यूशन भी करता है और लागू होने पर उन्हें अस्वीकार करता है।

+1

+1 जो उसने पूछा था, हालांकि यह बेहतर या खराब – Potatoswatter

+0

के लिए है, मैंने यह जानने के लिए पर्याप्त विविध टेम्पलेट्स के साथ नहीं खेला है कि प्रारंभिक परिवर्तनीय कहलाता है, लेकिन std :: array रूपांतरण +1 है, उस बारे में सोचा नहीं था। –

+1

हम्म, मुझे लगता है कि अस्वीकार-प्रारंभिक टेम्पलेट सामान्य मामले के लिए अच्छा नहीं है। यदि ड्रैगन बेस क्लास था, तो आपका पहला तर्क (या आपके सभी तर्क), एक व्युत्पन्न वर्ग हो सकता है, और एक दूसरे के लिए परिवर्तनीय नहीं है। जब ड्रैगन एक enum है, यह वही बात हो सकती है यदि आपकी पहली कक्षा पूर्णांक प्रकार में परिवर्तनीय है लेकिन एक पूर्णांक प्रकार नहीं है। मुझे लगता है कि ओवरलोडिंग पर भरोसा करना अधिक एक्स्टेंसिबल है। –

10

चूंकि आपने सी ++ 0x टैग शामिल किया है, तो स्पष्ट उत्तर initializer lists को देखना होगा। एक प्रारंभकर्ता सूची आपको एक ctor में कई तर्क निर्दिष्ट करने देती है जो स्वचालित रूप से ctor द्वारा प्रोसेसिंग के लिए एक डेटा संरचना में परिवर्तित हो जाएगी।

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

std::vector<dragon> dragons_to_slay{Eunice, Helga, Aida}; 

। अन्य संग्रहों में से अधिकांश (सभी?) में वही शामिल होगा, इसलिए यदि आप वास्तव में ड्रेगन की एक सूची पर जोर देते हैं तो आपको वह आसानी से आसानी से प्राप्त करने में सक्षम होना चाहिए।

+0

सुझाव के लिए धन्यवाद, इनिट सूचियों को देखने के लिए भी नहीं सोचा था। इन सभी उत्तरों पर अभी भी चबाने: \ –

1

यह वास्तव में निर्भर करता है कि आप क्या कार्यान्वित करने की कोशिश कर रहे हैं, बिल्कुल।

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

एक सीमित सेट के सबसेट का प्रतिनिधित्व करने का सबसे अच्छा तरीका एक बिटसेट है। बड़े सेट का उपयोग करना चाहिए; छोटे सेट केवल unsigned long का उपयोग कर सकते हैं।

enum Maiden_set { 
    Eunice = 1, 
    , Beatrice = 2 
    , Una_Brow = 4 
    , Helga = 8 
    , Aida = 16 
}; 

dragon_list_t make_dragon_list(Maiden_set) { 
    //here be dragons 
} 

make_dragon_list(Eunice + Beatrice + Helga); 

या, जब से तुम संकलन समय पर बदलाव के हैंडल करना चाहते हैं दिखाई देते हैं,

template< int Maidens > // parameter is not a Maiden_set because enum+enum=int 
dragon_list_t make_dragon_list() { 
    //here be dragons 
} 

make_dragon_list< Eunice + Beatrice + Helga >(); // + promotes each enum to int 

यह 2 स्वचालित रूप से एक operator+enum प्रकार पर अतिभारित का उपयोग करने का अधिकार है उत्पन्न करने के लिए संभव हो जाना चाहिए। लेकिन मुझे यकीन नहीं है कि मैं बिल्कुल सही रास्ते पर हूं।

+0

आप संकलित-समय बिट्सफ़िफ़्ट कर सकते हैं, यानी, enum {first_value = 1, second_value = 1 << 1, third_value = 1 << 2}; ' – Puppy

+0

@DeadMG: मैं लिखूंगा इस तरह, लेकिन आप आश्चर्यचकित होंगे कि कितने पाठकों ने पहले उस ऑपरेटर को कभी नहीं देखा है। – Potatoswatter

+0

मेरे पास है, लेकिन जिज्ञासा से बाहर, क्या बिट-एंडियननेस के बारे में कोई चिंता होगी? मुझे पता है कि शायद अस्पष्ट है। मुझे नहीं पता कि सीपीयू किस समस्या के साथ होंगे, और ऐसा नहीं है कि मैं कुछ ऐसा बनाने की कोशिश कर रहा हूं जो 100% क्रॉस-प्लेटफॉर्म या कुछ भी हो। बस उत्सुक। –

1

संक्षेप में, आपको शायद एक वेक्टर बनाना चाहिए। यह इतना ऊपरी नहीं है, खासकर अगर आप boost::list_of या सी ++ 0x की प्रारंभिक सूची जैसे कुछ का उपयोग करते हैं। सिंटैक्टिक ओवरहेड न्यूनतम है, और यह अधिक लचीला है (आप केवल रनटाइम पर ज्ञात कई तर्कों के साथ सूची पास कर सकते हैं)।

तुम सच में चाहते हैं, तो आप ऐसा करने variadic टेम्प्लेट पैरामीटर इस्तेमाल कर सकते हैं:

// Using pass-by-value since I'm assuming it is primitive: 

template< typename T, typename... Args> 
void make_dragon_list_internal(dragon_list_t* dragon_list, T t, Args... args) 
{ 
    // add T to dragon_list. 
    make_dragon_list_internal(dragon_list, args...); 
} 

void make_dragon_list_internal(dragon_list_t* dragon_list) 
{ 
    // Finalize dragon_list. 
} 

template<typename... Args> 
dragon_list_t make_dragon_list(Args... args) 
{ 
    dragon_list_t dragon_list; 
    make_dragon_list_internal(&dragon_list, args...); 
    return dragon_list; 
} 

यह typesafe है, और एक्स्टेंसिबल (आप इस ड्रेगन के अलावा अन्य चीजों से लेते हैं, यदि आप चाहें तो महसूस कर सकता है)।

+0

एर, और टाइपएफ़ द्वारा, मेरा मतलब है, टाइपएफ़ के रूप में अंतर्निहित प्रकार के रूप में, जो एक गैर-वर्ग enum के लिए बहुत अधिक नहीं है। ओपी देने के लिए –

1

मैं चीजों को सरल रखने की कोशिश करता हूं, और सबसे सरल समाधान जो मैं सोच सकता हूं वह केवल एक पुराने पुराने वेक्टर का उपयोग कर रहा है।

void foo(std::vector<int> const & v) { 
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); 
} 
int main() { 
    foo({ 1, 2, 3, 4, 5, 6 }); // note the extra {} 
} 
8

आप पैरामीटर नहीं पैक में variadic समारोह पर template उपयोग नहीं करते हैं: का उपयोग करके C++ 0x आप (यहां तक ​​कि बिल्कुल अगर) आप क्या चाहते हैं एक वाक्य रचना के समान है प्राप्त कर सकते हैं सुविधाएँ एक ही प्रकार के सभी तर्कों का समाधान करेगा।

यहां विस्तारित max फ़ंक्शन के लिए एक उदाहरण दिया गया है जो केवल int s (या परिवर्तनीय प्रकार int) स्वीकार करता है।

int maximum(int n) // last argument must be an `int` 
{ 
    return n; 
} 

template<typename... Args> 
int maximum(int n, Args... args) // first argument must be an int 
{ 
    return std::max(n, maximum(args...)); 
} 

स्पष्टीकरण: जब आप तर्क पैक (args...) खोल संकलक सबसे अच्छा अधिभार के लिए लग रहा है। यदि पैक में केवल एक पैरामीटर था तो केवल उम्मीदवार maximum(int) है, इसलिए एकमात्र पैरामीटर int (या परिवर्तनीय int) होना चाहिए। यदि पैक में एक से अधिक तत्व हैं तो एकमात्र उम्मीदवार maximum(int, typename...) है, इसलिए पहला तर्क int (या परिवर्तनीय int) होना चाहिए। प्रेरण से साबित करना आसान है कि पैक में सभी प्रकार int पर परिवर्तनीय प्रकार के होना चाहिए)।

+0

दिलचस्प ... यह नहीं कह सकता कि मैं समझता हूं कि यह क्यों काम करता है, उस पर कोई जानकारी/लिंक? –

+0

@pheadbaq, मैंने अपने उत्तर में एक स्पष्टीकरण जोड़ा है। – Motti

4

मुझे हाल ही में पैरामीटर पैक को केवल एक प्रकार के लिए बाध्य करने की आवश्यकता है, या उस प्रकार के कम से कम परिवर्तनीय।

#include <type_traits> 
#include <string> 

template <template<typename> class Trait, typename Head, typename ...Tail> 
struct check_all { 
    enum { value = Trait<Head>::value && check_all<Trait, Tail...>::value }; 
}; 

template <template<typename> class Trait, typename Head> 
struct check_all<Trait, Head> { 
    enum { value = Trait<Head>::value }; 
}; 

template <typename ...Args> 
struct foo { 
    // Using C++11 template alias as compile time std::bind 
    template <typename T> 
    using Requirement = std::is_convertible<double, T>; 
    static_assert(check_all<Requirement, Args...>::value, "Must convert to double"); 
}; 

int main() { 
    foo<int, char, float, double>(); 
    foo<int, std::string>(); // Errors, no conversion 
} 

बात यह है कि मैं इस समाधान के बारे में पसंद आया कि मैं भी अन्य लक्षण के लिए check_all लागू कर सकते हैं: मैं एक और तरीका खोजने समाप्त हो गया।

0

मुझे लगता है कि निम्नलिखित कोड अपने मामले के लिए उपयोगी है: हालांकि सवाल सी ++ 11 टैग है

template <class...> 
struct IsAllSame {}; 

template <class T, class B1> 
struct IsAllSame<T, B1> { 
    static constexpr const bool kValue = std::is_same<T, B1>::value; 
}; 

template <class T, class B1, class... Bn> 
struct IsAllSame<T, B1, Bn...> { 
    static constexpr const bool kValue = 
     IsAllSame<T, B1>::kValue ? IsAllSame<T, Bn...>::kValue : false; 
}; 

IsAllSame<int>::kValue == true 
IsAllSame<bool, int>::kValue == false 
IsAllSame<bool, int, int>::kValue == false 
1

, मुझे लगता है कि एक सी ++ 17 + अवधारणाओं समाधान के रूप में हालांकि वहाँ देखकर जोड़ने के लायक हो जाएगा अब जीसीसी में समर्थन है और अन्य जल्द ही पालन करेंगे।

पहले एक सरल अवधारणा को परिभाषित

class mytype{}; 

template<typename T> 
concept bool MyType = std::is_same<T, mytype>::value; 

तो बस variadic टेम्प्लेट पैरामीटर

template<MyType ... Args> 
void func(Args &&... args){ 
    // do something here 
} 

बहुत अवधारणाओं के आगमन के साथ आसान का उपयोग करें!

+0

अवधारणाओं ने इसे C++ 2a (अब तक) में बनाया है, इसमें यह शामिल नहीं है। [यहां देखें] (https://botondballo.wordpress.com/2017/08/02/trip-report-c- मानक-meeting-in-toronto-july-2017/#concepts)। – Noein