2014-10-15 16 views
13

से आधार टेम्पलेट की आभासी विधि को कॉल करना यह अनिवार्य रूप से an earlier question पर अनुवर्ती है (मेरे द्वारा उत्पन्न नहीं है, लेकिन मुझे एक उत्तर में रूचि है)।व्युत्पन्न विविधता टेम्पलेट वर्ग

सवाल यह है:क्यों संकलक/लिंकर व्युत्पन्न वर्ग से आभासी समारोह के लिए कॉल के समाधान में विफल करता है? इस मामले में, व्युत्पन्न वर्ग एक टेम्पलेट वर्ग है जिसमें वैरिएडिक पैरामीटर हैं जो एक ही टेम्पलेट वर्ग के विरुद्ध कई बार एकाधिक विरासत लागू करते हैं (एक बार विविध प्रकार के पैरामीटर में प्रत्येक प्रकार के लिए)।

नीचे ठोस उदाहरण में, व्युत्पन्न वर्ग JobPlant है, और इसे Worker से बुलाया जा रहा है। अमूर्त work() विधि को आमंत्रित करने के लिए workaround() लिंक और अपेक्षित तरीके से निष्पादित करते समय लिंक करने में विफल रहता है।

/home/g6xLmI/ccpFAanK.o: In function `main': 
prog.cpp:(.text.startup+0x8e): undefined reference to `Work<JobA>::work(JobA const&)' 
prog.cpp:(.text.startup+0xc9): undefined reference to `Work<JobB>::work(JobB const&)' 
prog.cpp:(.text.startup+0xff): undefined reference to `Work<JobC>::work(JobC const&)' 
collect2: error: ld returned 1 exit status 

Follow this link काम कर वैकल्पिक हल के प्रदर्शन के लिए:

ये लिंक विफलताओं के रूप में ideone द्वारा दिखाए गए हैं।

Job एक सार आधार वर्ग है, और यह व्युत्पन्न कक्षाओं से जुड़ा हुआ है। Work एक सार तत्व है जो एक नौकरी करता है।

struct Job { virtual ~Job() {} }; 

struct JobA : Job {}; 
struct JobB : Job {}; 
struct JobC : Job {}; 

template <typename JOB> 
struct Work { 
    virtual ~Work() {} 
    virtual void work(const JOB &) = 0; 
    void workaround(const Job &job) { work(dynamic_cast<const JOB &>(job)); } 
}; 

template <typename PLANT, typename... JOBS> struct Worker; 

template <typename PLANT, typename JOB, typename... JOBS> 
struct Worker<PLANT, JOB, JOBS...> { 
    bool operator()(PLANT *p, const Job &job) const { 
     if (Worker<PLANT, JOB>()(p, job)) return true; 
     return Worker<PLANT, JOBS...>()(p, job); 
    } 
}; 

template <typename PLANT, typename JOB> 
struct Worker<PLANT, JOB> { 
    bool operator()(PLANT *p, const Job &job) const { 
     if (dynamic_cast<const JOB *>(&job)) { 
      p->Work<JOB>::work(dynamic_cast<const JOB &>(job)); 
      //p->Work<JOB>::workaround(job); 
      return true; 
     } 
     return false; 
    } 
}; 

एक JobPlant कि एक Worker पाता है एक job प्रदर्शन करने के लिए एक टेम्पलेट वर्ग JOBS द्वारा parameterized, है: Worker एक (structclass बजाय विशुद्ध रूप से वाक्य रचना अव्यवस्था कम करने) टेम्पलेट कि JOB को दिखाता है और यह करता है। JOBS में प्रत्येक नौकरी प्रकार के लिए Work से विरासत प्राप्त करता है। MyJobPlantJobPlant का एक उदाहरण है, और संबंधित Work सार वर्गों से वर्चुअल work विधियों को लागू करता है।

template <typename... JOBS> 
struct JobPlant : Work<JOBS>... { 
    typedef Worker<JobPlant, JOBS...> WORKER; 
    bool worker(const Job &job) { return WORKER()(this, job); } 
}; 

struct MyJobPlant : JobPlant<JobA, JobB, JobC> { 
    void work(const JobA &) { std::cout << "Job A." << std::endl; } 
    void work(const JobB &) { std::cout << "Job B." << std::endl; } 
    void work(const JobC &) { std::cout << "Job C." << std::endl; } 
}; 

int main() { 
    JobB j; 
    MyJobPlant().worker(j); 
} 

उत्तर

9

आप स्पष्ट रूप से p->Work<JOB>::work() फोन, कि है, Work<JOB> में शुद्ध आभासी विधि। यह विधि लागू नहीं की गई है (यह सब के बाद शुद्ध है)।

इससे कोई फर्क नहीं पड़ता कि व्युत्पन्न कक्षाएं उस विधि को लागू/ओवरराइड कर सकती हैं। Work<JOB>:: कहता है कि आप उस वर्ग के संस्करण को चाहते हैं, व्युत्पन्न वर्ग से कुछ नहीं। कोई गतिशील प्रेषण नहीं होता है।

(Work<JOB>::work() वाक्य रचना आप एक व्युत्पन्न वर्ग में एक अधिभावी विधि से एक आधार वर्ग विधि कॉल करने का प्रयोग करेंगे है, और तुम सच में आधार वर्ग विधि चाहते हैं।)


जब आप तो स्पष्ट हटाने Work<JOB>::, परिणाम एक अस्पष्टता त्रुटि है। work नाम को हल करने का प्रयास करते समय, संकलक पहले यह निर्धारित करने का प्रयास करता है कि किन वर्गों में से कौन सा नाम है। इसके बाद, अगला चरण तब उस श्रेणी में विभिन्न work विधियों के बीच ओवरलोड रिज़ॉल्यूशन होगा।

दुर्भाग्यवश अस्पष्टता में पहला चरण परिणाम: एक से अधिक बेस क्लास में work नाम शामिल है। कंपाइलर तब मेल खाने वाले अधिभार को समझने की कोशिश नहीं करता है।(वे वास्तव में अधिभारित नहीं हैं बल्कि एक-दूसरे के साथ संघर्ष कर रहे हैं, क्योंकि वे विभिन्न वर्गों से हैं)।

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

विविधता टेम्पलेट चीजों को जटिल करता है क्योंकि मुझे नहीं लगता कि using Work<JOBS>::work...; कानूनी है (और मेरा कंपाइलर ऐसा नहीं सोचता है)।

template <typename JOB, typename... JOBS> 
struct CollectWork : Work<JOB>, CollectWork<JOBS...> { 
    using Work<JOB>::work; 
    using CollectWork<JOBS...>::work; 
}; 

template <typename JOB> 
struct CollectWork<JOB> : Work<JOB> { 
    using Work<JOB>::work; 
}; 

template<typename... JOBS> 
struct JobPlant : CollectWork<JOBS...> {           
    using CollectWork<JOBS...>::work; 
    typedef Worker<JobPlant, JOBS...> WORKER; 
    bool worker(const Job &job) { return WORKER()(this, job); } 
}; 
इस संरचना के साथ

, समस्याग्रस्त p->work(dynamic_cast<const JOB &>(job));compiles and runs successfully: लेकिन अगर आप आधार वर्ग रचना "मैन्युअल", सभी काम के तरीकों अंतिम कक्षा में लाया जा सकता है।

+0

हू। वह आसान था। क्या आप समझा सकते हैं कि 'p-> काम क्यों करें (dynamic_cast (नौकरी))' अस्पष्टता त्रुटि देता है? देखें: http://ideone.com/2vL57z – jxh

+0

@jxh: मेरा संपादन देखें :) – sth

+2

'पी-> कार्य :: कोड()' को निम्नलिखित कोड द्वारा हल करने से समस्या का समाधान भी हो जाता है। कार्य और डब्ल्यू = * पी; w.work (dynamic_cast (नौकरी)); – JVApen

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