2016-04-09 6 views
11

मान लें कि मुझे टेम्पलेट, टी 1 और टी 2 में दो तर्क प्राप्त हैं। अगर मुझे पता है कि टी 1 स्वयं ही एक टेम्पलेटेड क्लास (उदाहरण के लिए, एक कंटेनर) है, और टी 2 कुछ भी हो सकता है, तो क्या मेरे लिए टी 1 के लिए बेस टेम्पलेट प्रकार निर्धारित करना संभव है और टी 2 का उपयोग करके इसे तर्क के रूप में पुनर्निर्माण करना संभव है?क्या सी ++ में अपने तर्कों से टेम्पलेट को विचलित करना संभव है?

उदाहरण के लिए, यदि मुझे std::vector<int> और std::string प्राप्त होता है, तो मैं स्वचालित रूप से std::vector<std::string> बनाना चाहता हूं। हालांकि अगर मुझे std::set<bool> और double दिया गया था, तो यह std::set<double> का उत्पादन करेगा।

टाइप_ट्रेट, प्रासंगिक ब्लॉग और अन्य प्रश्नों की समीक्षा करने के बाद, मुझे इस समस्या को हल करने के लिए एक सामान्य दृष्टिकोण नहीं दिख रहा है। इस कार्य को पूरा करने के लिए वर्तमान में एकमात्र तरीका यह है कि प्रत्येक प्रकार के लिए टेम्पलेट एडेप्टर बनाना है जिसे टी 1 के रूप में पारित किया जा सकता है।

उदाहरण के लिए, अगर मैं था:

template<typename T_inner, typename T_new> 
std::list<T_new> AdaptTemplate(std::list<T_inner>, T_new); 

template<typename T_inner, typename T_new> 
std::set<T_new> AdaptTemplate(std::set<T_inner>, T_new); 

template<typename T_inner, typename T_new> 
std::vector<T_new> AdaptTemplate(std::vector<T_inner>, T_new); 

मैं decltype का उपयोग करें और अपनी समस्या को हल करने के अधिक भार ऑपरेटर पर भरोसा करने में सक्षम होना चाहिए। कुछ के साथ कुछ:

template <typename T1, typename T2> 
void MyTemplatedFunction() { 
    using my_type = decltype(AdaptTemplate(T1(),T2())); 
} 

क्या मुझे कुछ याद आ रही है? क्या कोई बेहतर दृष्टिकोण है?

मैं ऐसा क्यों करना चाहता हूं?

मैं एक सी ++ लाइब्रेरी का निर्माण कर रहा हूं जहां मैं सरल बनाना चाहता हूं कि मॉड्यूलर टेम्पलेट्स बनाने के लिए उपयोगकर्ताओं को क्या करना है। उदाहरण के लिए, यदि कोई उपयोगकर्ता एजेंट-आधारित सिमुलेशन बनाना चाहता है, तो वे एक विश्व टेम्पलेट को जीव प्रकार, जनसंख्या प्रबंधक, पर्यावरण प्रबंधक और एक व्यवस्थित प्रबंधक के साथ कॉन्फ़िगर कर सकते हैं।

World< NeuralNetworkAgent, EAPop<NeuralNetworkAgent>, 
     MazeEnvironment<NeuralNetworkAgent>, 
     LineageTracker<NeuralNetworkAgent> > world; 

मैं बहुत चाहते बल्कि उन NeuralNetworkAgent हर बार दोहराना नहीं:

प्रबंधकों की

प्रत्येक भी जीव प्रकार पता करने के लिए है, तो एक घोषणा कुछ कैसा लग सकता है की जरूरत है। अगर मैं टेम्पलेट तर्क को बदलने में सक्षम हूं, तो डिफ़ॉल्ट तर्क का इस्तेमाल किया जा सकता है और इसके बाद के संस्करण के लिए सरल किया जा सकता:

World< NeuralNetworkAgent, EAPop<>, MazeEnvironment<>, LineageTracker<> > world; 

साथ ही यह प्रकार त्रुटियों के बारे में चिंता किए बिना एक से दूसरे विश्व प्रकार से परिवर्तित करने के लिए आसान है।

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

+0

मुझे मिले सभी तीन उत्तरों उत्कृष्ट थे। @ बैरी द्वारा एक सबसे गहन है और टीसी द्वारा एक है। मेरी अंतर्निहित समस्या को सबसे अच्छा हल करता है, लेकिन मुझे लगता है कि सैम वरशाविक (जिसे मैंने स्वीकार किया) की प्रतिक्रिया प्रश्न को हल करने के लिए सबसे खूबसूरत है जैसा मैंने पूछा था। सभी को बहुत बहुत धन्यवाद! –

+0

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

+0

हालांकि दूसरा प्रश्न कम व्यापक है, उत्तर आपके मामले को पूरी तरह से कवर करते हैं। यही कारण है कि मेरा झंडा एक बैनर में कहता है कि "आपके प्रश्न का जवाब यहां पहले से है"। –

उत्तर

3

इस तरीके से आप के बारे में पूछ रहे हैं, जीसीसी 5.3.1 के साथ परीक्षण में काम करने के लिए लगता है:

#include <vector> 
#include <string> 

template<typename T, typename ...U> class AdaptTemplateHelper; 

template<template <typename...> class T, typename ...V, typename ...U> 
class AdaptTemplateHelper<T<V...>, U...> { 
public: 

    typedef T<U...> type; 
}; 

template<typename T, typename ...U> 
using AdaptTemplate=typename AdaptTemplateHelper<T, U...>::type; 

void foo(const std::vector<std::string> &s) 
{ 
} 

int main() 
{ 
    AdaptTemplate<std::vector<int>, std::string> bar; 

    bar.push_back("AdaptTemplate"); 
    foo(bar); 
    return 0; 
} 

बेस्ट सी ++ सवाल इस सप्ताह।

+0

हर बार जब मुझे लगता है कि मैं सूक्ष्म मेटा-प्रोग्रामिंग मुद्दों से निपटने में सहज महसूस कर रहा हूं और सी ++ की सीमाओं को जान रहा हूं, तो मैं यहां एक प्रश्न पूछता हूं और कुछ नया सीखता हूं। धन्यवाद! –

3

यह मूल रूप से दो अलग-अलग समस्याएं हैं: क्लास टेम्पलेट में क्लास टेम्पलेट के तत्कालता को कैसे विघटित करना है, और उसके बाद क्लास टेम्पलेट कैसे लेना है और इसे तुरंत चालू करना है। आइए सिद्धांत के साथ जाएं कि टेम्पलेट मेटाप्रोग्रामिंग आसान है अगर सबकुछ हमेशा एक प्रकार होता है।

पहला, दूसरा भाग।एक वर्ग टेम्पलेट को देखते हुए, यह एक metafunction वर्ग में बदल जाते हैं करते हैं:

template <template <typename...> class F> 
struct quote { 
    template <typename... Args> 
    using apply = F<Args...>; 
}; 

यहाँ, quote<std::vector> एक metafunction वर्ग है। यह एक ठोस प्रकार है जिसमें सदस्य टेम्पलेट apply है। तो quote<std::vector>::apply<int> आपको std::vector<int> देता है।

अब, हमें एक प्रकार को अनपैक करने की आवश्यकता है। आइए इसे unquote पर कॉल करें (कम से कम यह मेरे लिए उचित लगता है)।

: तुम सब करने की जरूरत है unquote में इन्स्टेन्शियशन गुजरती हैं और उपलब्ध कराने के नए आर्ग आप metafunction वर्ग में चाहते हैं कि उसे बाहर थूक है

template <class > 
struct unquote; 

template <class T> 
using unquote_t = typename unquote<T>::type; 

template <template <typename...> class F, typename... Args> 
struct unquote<F<Args...>> { 
    using type = quote<F>; 
}; 

अब: यह एक metafunction कि एक प्रकार लेता है और एक metafunction वर्ग पैदावार है

unquote_t<std::vector<int>>::apply<std::string> 

अपने विशिष्ट मामले के लिए, बस quote सब कुछ:

// I don't know what these things actually are, sorry 
template <class Agent, class MF1, class MF2, class MF3> 
struct World { 
    using t1 = MF1::template apply<Agent>; 
    using t2 = MF2::template apply<Agent>; 
    using t3 = MF3::template apply<Agent>; 
}; 


World< NeuralNetworkAgent, 
    quote<EAPop>, 
    quote<MazeEnvironment>, 
    quote<LineageTracker> 
> w; 
+1

इस दृष्टिकोण के साथ समस्या यह है कि आप गैर-डिफ़ॉल्ट आवंटकों, तुलनित्र आदि का उपयोग नहीं कर सकते हैं –

+0

@ टी.सी. तुलनात्मक रूप से स्वैप करने के लिए संदिग्ध हैं, अगर तुलनित्र टेम्पलेट नहीं है तो क्या होगा? इसे स्पष्ट रूप से प्रदान किया जाना बेहतर है। आवंटक के साथ, निश्चित रूप से, मैं सिर्फ एक अलग तरह का 'उद्धरण' लिख सकता हूं, यह एक बड़ा सौदा नहीं है। – Barry

+1

@ टी.सी. अन्य समाधान संख्या के लिए वही टिप्पणी? – Barry

3

आपकी वास्तविक समस्या को केवल टेम्पलेट टेम्पलेट पैरामीटर ले कर हल किया जा सकता है।

template <class Agent, template<class...> class F1, 
         template<class...> class F2, 
         template<class...> class F3> 
struct World { 
    // use F1<Agent> etc. 
}; 

World<NeuralNetworkAgent, EAPop, MazeEnvironment, LineageTracker > world; 

@ बैरी quote ऐसा करने का एक अधिक सजावटी रास्ता है, जो और अधिक जटिल metaprogramming के लिए उपयोगी है, लेकिन एक का उपयोग मामला इस सरल के लिए IMO overkill है।

टेम्पलेट तर्कों के एक अलग सेट पर मनमाने ढंग से टेम्पलेट विशेषज्ञता को रिबाइंड करना सी ++ में संभव नहीं है; अधिकतर आप एक सबसेट (मुख्य रूप से टेम्पलेट्स केवल टाइप पैरामीटर लेते हैं, साथ ही कुछ अन्य संयोजन जिन्हें आप समर्थन के लिए चुन सकते हैं) से निपट सकते हैं, और फिर भी कई समस्याएं हैं। std::unordered_set<int, my_fancy_hash<int>, std::equal_to<>, std::pmr::polymorphic_allocator<int>> को सही ढंग से पुन: उपयोग करने के लिए उपयोग किए गए टेम्पलेट्स के लिए विशिष्ट ज्ञान की आवश्यकता होती है।

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