गतिशील बहुरूपता से बचने के लिए CRTP


जवाबों:


139

इसके दो तरीके हैं।

पहला एक प्रकार की संरचना के लिए इंटरफ़ेस को निर्दिष्ट करके है:

template <class Derived>
struct base {
  void foo() {
    static_cast<Derived *>(this)->foo();
  };
};

struct my_type : base<my_type> {
  void foo(); // required to compile.
};

struct your_type : base<your_type> {
  void foo(); // required to compile.
};

दूसरा एक संदर्भ-से-आधार या पॉइंटर-टू-बेस मुहावरे के उपयोग से बचने और संकलन-समय पर वायरिंग करने के लिए है। उपरोक्त परिभाषा का उपयोग करते हुए, आपके पास इस तरह दिखने वाले टेम्प्लेट फ़ंक्शंस हो सकते हैं:

template <class T> // T is deduced at compile-time
void bar(base<T> & obj) {
  obj.foo(); // will do static dispatch
}

struct not_derived_from_base { }; // notice, not derived from base

// ...
my_type my_instance;
your_type your_instance;
not_derived_from_base invalid_instance;
bar(my_instance); // will call my_instance.foo()
bar(your_instance); // will call your_instance.foo()
bar(invalid_instance); // compile error, cannot deduce correct overload

इसलिए संरचना / इंटरफ़ेस की परिभाषा और आपके कार्यों में संकलन-समय प्रकार की कटौती आपको गतिशील प्रेषण के बजाय स्थिर प्रेषण करने की अनुमति देती है। यह स्थैतिक बहुरूपता का सार है।


15
उत्कृष्ट उत्तर
एली बेंडरस्की

5
मैं इस बात पर ज़ोर देना चाहता हूं कि not_derived_from_baseयह व्युत्पन्न नहीं है base, और न ही यह व्युत्पन्न है base...
लेफ्टरनैबाउट

3
दरअसल, my_type / your_type के अंदर foo () की घोषणा की आवश्यकता नहीं है। codepad.org/ylpEm1up (कारण ढेर अतिप्रवाह) - क्या संकलन के समय फू की परिभाषा लागू करने का कोई तरीका है? - ठीक है, एक समाधान मिला: ideone.com/C6Oz9 - शायद आप अपने उत्तर में इसे सही करना चाहते हैं।
cooky451

3
क्या आप मुझे बता सकते हैं कि इस उदाहरण में CRTP का उपयोग करने की प्रेरणा क्या है? यदि बार को टेम्पलेट के रूप में परिभाषित किया जाएगा <class T> void bar (T & obj) {obj.foo (); }, फिर कोई भी वर्ग जो फू प्रदान करता है वह ठीक होगा। तो आपके उदाहरण के आधार पर ऐसा लगता है कि संकलन समय पर इंटरफ़ेस को निर्दिष्ट करने के लिए CRTP का एकमात्र उपयोग है। यह है कि यह किस लिए है?
एंटोन डेनेको

1
@ डीन माइकल वास्तव में उदाहरण में कोड संकलन करता है, भले ही foo को my_type और your_type में परिभाषित नहीं किया गया हो। उन लोगों के बिना आधार को ओवरराइड किया जाता है :: foo को पुनरावर्ती रूप से (और स्टैकओवरफ्लो) कहा जाता है। तो हो सकता है कि आप सही उत्तर देना चाहते हों, जैसा कि कुकिउ ने दिखाया है?
एंटन डेनेको

18

मैं खुद CRTP की अच्छी चर्चा कर रहा हूँ। टॉड वेल्डहुइज़न की तकनीक फॉर साइंटिफिक C ++ इसके लिए एक बेहतरीन संसाधन (1.3) और कई अन्य उन्नत तकनीकों जैसे एक्सप्रेशन टेम्प्लेट हैं।

इसके अलावा, मैंने पाया कि आप Google पुस्तकों में अधिकांश कोप्लिन के मूल C ++ रत्न लेख को पढ़ सकते हैं। शायद अभी भी यही है।


@fizzer मैंने आपके द्वारा सुझाए गए भाग को पढ़ा है, लेकिन फिर भी समझ में नहीं आता है कि टेम्पलेट <class T_leaftype> double sum (मैट्रिक्स <T_leaftype> & A) क्या है? आपको टेम्पलेट की तुलना में खरीदता है <क्लास जो भी> डबल राशि (जो भी & ए);
एंटोन डेनेको

@AntonDaneyko जब एक बेस इंस्टेंस पर कॉल किया जाता है, बेस क्लास का योग कहा जाता है, उदाहरण के लिए "एक आकार का क्षेत्र" डिफ़ॉल्ट कार्यान्वयन के साथ जैसे कि यह एक वर्ग था। इस मामले में CRTP का लक्ष्य सबसे अधिक व्युत्पन्न कार्यान्वयन, "एक ट्रेपोज़ॉइड का क्षेत्र" आदि को हल करना है, जबकि अभी भी व्युत्पन्न व्यवहार की आवश्यकता होने तक एक आकृति के रूप में ट्रेपोज़ॉइड को संदर्भित करने में सक्षम है। मूल रूप से, जब भी आपको सामान्य रूप से dynamic_castया आभासी तरीकों की आवश्यकता होगी ।
जॉन पी।

1

मुझे CRTP देखना था । हालांकि, ऐसा करने पर मुझे स्टैटिक पॉलीमॉर्फिज़्म के बारे में कुछ चीजें मिलीं । मुझे संदेह है कि यह आपके प्रश्न का उत्तर है।

यह पता चला है कि एटीएल इस पैटर्न का काफी उपयोग करता है।


-5

इस विकिपीडिया उत्तर की आपको सभी आवश्यकता है। अर्थात्:

template <class Derived> struct Base
{
    void interface()
    {
        // ...
        static_cast<Derived*>(this)->implementation();
        // ...
    }

    static void static_func()
    {
        // ...
        Derived::static_sub_func();
        // ...
    }
};

struct Derived : Base<Derived>
{
    void implementation();
    static void static_sub_func();
};

हालांकि मुझे नहीं पता कि यह वास्तव में आपको कितना खरीदता है। वर्चुअल फ़ंक्शन कॉल का ओवरहेड (कंपाइलर निर्भर, निश्चित रूप से) है:

  • मेमोरी: एक फ़ंक्शन पॉइंटर प्रति वर्चुअल फ़ंक्शन
  • रनटाइम: एक फ़ंक्शन पॉइंटर कॉल

जबकि CRTP स्थैतिक बहुरूपता का उपरी भाग है:

  • मेमोरी: बेस प्रति डुप्लीकेशन इंस्टेंटेशन का दोहराव
  • रनटाइम: एक फ़ंक्शन पॉइंटर कॉल + जो static_cast कर रहा है

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

19
वैसे, CRTP का आपका विश्लेषण गलत है। यह होना चाहिए: मेमोरी: कुछ भी नहीं, जैसा कि डीन माइकल ने कहा। रनटाइम: एक (तेज) स्टैटिक फंक्शन कॉल, वर्चुअल नहीं, जो व्यायाम का पूरा बिंदु है। static_cast कुछ भी नहीं करता है, यह सिर्फ कोड को संकलित करने की अनुमति देता है।
फ्रेडरिक स्लीजकरमैन

2
मेरा कहना है कि आधार कोड को सभी टेम्पलेट इंस्टेंसेस (आप जिस विलय की बात करते हैं) में दोहराया जाएगा। केवल एक विधि के साथ एक टेम्पलेट होने के लिए अकिन जो टेम्पलेट पैरामीटर पर निर्भर करता है; बेस क्लास में बाकी सब बेहतर है अन्यथा इसे कई बार ('मर्ज') में खींच लिया जाता है।
user23167

1
आधार में प्रत्येक विधि को प्रत्येक व्युत्पन्न के लिए फिर से संकलित किया जाएगा। अपेक्षित (अपेक्षित) मामले में जहां प्रत्येक तात्कालिक विधि भिन्न होती है (क्योंकि व्युत्पन्न होने के गुण अलग-अलग होते हैं), जिसे आवश्यक रूप से उपरि के रूप में नहीं गिना जा सकता है। लेकिन यह बड़े समग्र कोड आकार, बनाम उस स्थिति को जन्म दे सकता है जहां आधार (सामान्य) आधार वर्ग में एक जटिल विधि उपवर्गों के आभासी तरीकों को बुलाती है। इसके अलावा, यदि आप आधार में उपयोगी विधियां डालते हैं <Derived>, जो वास्तव में <Derived> पर बिल्कुल भी निर्भर नहीं करते हैं, तो भी वे तुरंत चालू हो जाएंगे। शायद वैश्विक अनुकूलन कुछ हद तक ठीक कर देगा।
ग्रोगो

एक कॉल जो CRTP की कई परतों से होकर गुज़रती है, संकलन के दौरान मेमोरी में विस्तार करेगी लेकिन TCO और इनलाइनिंग के माध्यम से आसानी से अनुबंध कर सकती है। CRTP ही वास्तव में अपराधी नहीं है, है ना?
जॉन पी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.