2012-12-13 16 views
15

पर कॉल करता है मैं एक वैरैडिक टेम्पलेटेड क्लास बनाने की कोशिश कर रहा हूं जो टाइपलिस्ट में प्रत्येक वर्ग के लिए एक विधि प्रदान करता है। टिप्पणी की लाइन पर जीसीसी 4.6.3 सेसंदिग्ध फ़ंक्शन C++ बेस क्लास

#include <iostream> 
#include <string> 

// Helper class providing a function call 
template <typename T> 
class PrintHelper 
{ 
public: 
    void print(const T& t) { std::cout << t << std::endl; } 
}; 

// Provides a print method for each type listed 
template <typename... Ts> 
class Printer : public PrintHelper<Ts>... 
{}; 

int main() 
{ 
    Printer<int, std::string> p; 
    p.print(std::string("Hello World")); // Ambiguous Call 
} 

एक त्रुटि में टिप्पणी की लाइन परिणाम: एक उदाहरण जो typelist में हर वर्ग के लिए एक print विधि बनाता है नीचे दिखाया गया है। अस्पष्टता को हल करने का सही तरीका क्या है या क्या मुझे एक अलग डिज़ाइन को देखना चाहिए?

+0

जीसीसी 4.7.2 ([liveworkspace] पर (http://liveworkspace.org/code/1a4YN8$0) के साथ एक ही समस्या। कॉपी/पेस्टेबल उदाहरण बीटीडब्ल्यू के लिए धन्यवाद। –

+1

क्लैंग 3.2 ट्रंक 165721 कहता है: 'त्रुटि: सदस्य' प्रिंट 'विभिन्न प्रकार के कई आधार वर्गों में पाया गया है – user786653

+2

यह भी देखें [यह प्रश्न] (http://stackoverflow.com/questions/5368862/why-do-multiple-herherited -functions-साथ-ही-नाम बल्कि अलग-हस्ताक्षर-नहीं)। यह संक्षेप में छुपा नियमों के संयोजन की समस्या और अनपॅकिंग वैरिएड टेम्पलेट तर्कों की बहुत सीमित क्षमताओं में है। –

उत्तर

8

मुझे अपने स्वयं के प्रश्न का उत्तर देना पसंद नहीं है, लेकिन मैंने यहां टिप्पणियों से निम्नलिखित समाधान बनाया है। यह सभी print कार्यों को दायरे में लाता है और सभी कार्यों पर सी ++ ओवरलोड रिज़ॉल्यूशन की अनुमति देता है।

#include <iostream> 
#include <string> 

// Helper class providing a function call 
template <typename T> 
class PrintHelper 
{ 
public: 
    void print(const T& t) { std::cout << t << std::endl; } 
}; 

// Provides a print method for each type listed 
template <typename... Ts> 
class Printer 
{}; 

template<typename T> 
class Printer<T> : public PrintHelper<T> 
{ 
public: 
    using PrintHelper<T>::print; 
}; 

template<typename T, typename... Ts> 
class Printer<T, Ts...>: public PrintHelper<T>, public Printer<Ts...> 
{ 
public: 
    using PrintHelper<T>::print; 
    using Printer<Ts...>::print; 
}; 

int main() 
{ 
    Printer<int, std::string> p; 
    p.print("Hello World"); // Not an ambiguous Call 
} 
+0

+1 यह बढ़िया है, मैंने इसके बारे में क्यों नहीं सोचा था ... तो मुझे लगता है कि आप विविध टेम्पलेट्स के लिए उपयोग-घोषणाओं को अनपैक कर सकते हैं। –

+0

अपने स्वयं के प्रश्न का जवाब देना बिल्कुल ठीक है (और यहां तक ​​कि स्पष्ट रूप से प्रोत्साहित)। यह भविष्य के पाठकों के लिए लाभदायक है। सी एफ http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/। इसके अलावा आपके द्वारा प्रस्तावित समाधान स्वीकार्य एक इम्हो होना चाहिए। –

9

अस्पष्टता को हल करने के लिए यह संभव है

template <typename... Ts> 
struct Printer : PrintHelper<Ts>... 
{ 
    template <typename U> 
    void print (const U& t) 
    { 
     PrintHelper<U>::print (t); 
    } 
}; 

(an example देखें)

ऐसा करने के लिए, लेकिन यह काफी के रूप में मजबूत के रूप में एक आशा नहीं है। विशेष रूप से, आप किसी ऑब्जेक्ट को प्रिंट नहीं कर सकते जो कि प्रकार सूची में से किसी एक में परिवर्तनीय है।

कुछ टेम्पलेट मेटाप्रोग्रामिंग के साथ, हालांकि सही प्रिंटर को प्रेषण करना संभव है। ऐसा करने के लिए आपको Ts... से एक प्रकार का चयन करना होगा जिसमें U परिवर्तनीय है, और सही PrintHelper, यानी सही कॉल करें।

PrintHelper<typename find_convertible<U, Ts...>::type>::print (t); 

जहां find_convertible<U, Ts...>

द्वारा परिभाषित किया गया
template <typename U, typename... Ts> 
struct find_convertible 
{}; 

template <typename U, typename V, typename... Ts> 
struct find_convertible<U, V, Ts...> : 
    std::conditional< 
     std::is_convertible<U, V>::value, 
     std::common_type<V>, // Aka identity 
     find_convertible<U, Ts...> 
    >::type 
{}; 

(example देखें)

+0

कोड की सुंदर शांति! –

+0

सहायता के लिए धन्यवाद। पहला उदाहरण मेरे उपयोग के मामले के लिए पर्याप्त है। उपर्युक्त टिप्पणियों में से एक के बाद विरासत को बदलकर रूपांतरणों को अनुमति देने का एक और संभावित समाधान है (http://liveworkspace.org/code/2PpiGS$1)। यह आपकी विधि के आधार पर सी ++ ओवरलोड रिज़ॉल्यूशन का उपयोग करेगा जो टाइपलिस्ट में पहला परिवर्तनीय प्रकार लेगा। –

+0

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

3

निम्नलिखित कोड अस्पष्टता समस्या को हल कर सकते हैं:

#include <iostream> 
#include <string> 

// Helper class providing a function call 
template <typename T> 
class PrintHelper 
{ 
    protected: 
    void print_impl(const T& t) { std::cout << t << std::endl; } 
}; 

// Provides a print method for each type listed 
template <typename... Ts> 
class Printer : public PrintHelper<Ts>... 
{ 
    public: 
    template <typename U> 
    void print(const U& u) { 
     PrintHelper<U>::print_impl(u); 
    }; 
}; 

int main() 
{ 
    Printer<int, std::string> p; 
    p.print(std::string("Hello World")); // Ambiguous Call 
} 

कौन सा बहुत अच्छा नहीं है की वजह से आवश्यकता यू कि प्रकार (कॉल पर कटौती) वें वें में से एक है विविध प्रकार की सूची में ई प्रकार। आप उस मुद्दे को हल करने के लिए चीजों को थोड़ा सा कल्पना करने में सक्षम हो सकते हैं। टेम्पलेट जादू और Sfinae के साथ, आप शायद यह काफी आसानी से हल कर सकते हैं (लेकिन यह निश्चित रूप से साफ और साफ नहीं है)।

अस्पष्टता समस्या टेम्पलेट या वैरिएडिक टेम्पलेट्स के उपयोग से संबंधित नहीं है, बेशक, यह सदस्य लुकअप नियमों (मानक के अनुभाग 10.2/2) का एक सीधा-आगे आवेदन है, यानी, अक्सर तथाकथित "सदस्य छिपाने के नियम"। आप इस सरल गैर टेम्पलेट संस्करण ले, तो आप एक ही अस्पष्टता समस्या मिल जाएगा, लेकिन यह करने के लिए एक बहुत ही सरल समाधान के साथ:

struct IntPrinter { 
    void print(const int& i) { std::cout << i << std::endl; }; 
}; 

struct StringPrinter { 
    void print(const std::string& s) { std::cout << s << std::endl; }; 
}; 

struct IntStringPrinter : IntPrinter, StringPrinter { 
    using IntPrinter::print;  // These using-statements will solve the problem 
    using StringPrinter::print; // by importing all 'print' functions to the same 
           // overload resolution level. 
}; 

तो, समस्या यहाँ वास्तव में है कि अस्पष्टता उठता संकलक से पहले भी कोशिश करता है सामान्य अधिभार-रिज़ॉल्यूशन नियमों को लागू करने के लिए, क्योंकि यह पहले यह पता लगाने की कोशिश करता है कि सदस्य कार्य (ओं) को खोजने के लिए विरासत की कौन सी शाखा का पालन करना है, और फिर, यह केवल एक विरासत स्तर पर अधिभार को हल करेगा। और भिन्नता टेम्पलेट्स का उपयोग करते समय समस्या यह है कि समानता स्तर तक सभी प्रिंट फ़ंक्शंस आयात करने के लिए "उपयोग" कथन के सेट को अनपैक करने का कोई तरीका नहीं प्रतीत होता है। अगर कोई इस तरह के उपयोग-बयान को अनपैक करने के तरीके के बारे में जानता है, तो मैं सभी कान हूं। आपको प्री-वैरैडिक-टेम्पलेट समाधान (10 तर्कों के साथ एक सामान्य टेम्पलेट की तरह, और सभी दस संस्करणों के लिए विशेषज्ञ) पर वापस आना पड़ सकता है।

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