खोज…


परिचय

एक पैटर्न जिसमें एक वर्ग अपने स्वयं के टेम्पलेट मापदंडों में से एक के रूप में एक वर्ग टेम्पलेट से विरासत में मिला है। CRTP का उपयोग आमतौर पर C ++ में स्थैतिक बहुरूपता प्रदान करने के लिए किया जाता है।

जिज्ञासु आवर्ती टेम्पलेट पैटर्न (CRTP)

CRTP वर्चुअल फ़ंक्शंस और पारंपरिक वंशानुक्रम का एक शक्तिशाली, स्थिर विकल्प है जिसका उपयोग संकलन समय पर प्रकार के गुण देने के लिए किया जा सकता है। यह बेस क्लास टेम्प्लेट होने से काम करता है, जो अपने टेम्प्लेट मापदंडों में से एक के रूप में लेता है, व्युत्पन्न वर्ग। यह परमिट कानूनी तौर पर एक प्रदर्शन करने के लिए static_cast इसके की this व्युत्पन्न वर्ग के लिए सूचक।

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

सी ++ 14

मान लें कि आपके पास कंटेनरों का एक सेट है जो सभी कार्यों को begin() और end() समर्थन करता है। कंटेनरों के लिए मानक पुस्तकालय की आवश्यकताओं को अधिक कार्यक्षमता की आवश्यकता होती है। हम एक CRTP बेस क्लास डिजाइन कर सकते हैं जो कि कार्यक्षमता प्रदान करता है, जो केवल begin() और end() पर आधारित है:

#include <iterator>
template <typename Sub>
class Container {
  private:
    // self() yields a reference to the derived type
    Sub& self() { return *static_cast<Sub*>(this); }
    Sub const& self() const { return *static_cast<Sub const*>(this); }

  public:
    decltype(auto) front() {
      return *self().begin();
    }

    decltype(auto) back() {
      return *std::prev(self().end());
    }

    decltype(auto) size() const {
      return std::distance(self().begin(), self().end());
    }

    decltype(auto) operator[](std::size_t i) {
      return *std::next(self().begin(), i);
    }
};

उपरोक्त वर्ग किसी भी उपवर्ग के लिए कार्य front() , back() , size() , और operator[] प्रदान करता है जो begin() और end() प्रदान करता है। एक उपवर्ग एक सरल गतिशील रूप से आवंटित सरणी है:

#include <memory>
// A dynamically allocated array
template <typename T>
class DynArray : public Container<DynArray<T>> {
  public:
    using Base = Container<DynArray<T>>;

    DynArray(std::size_t size)
      : size_{size},
      data_{std::make_unique<T[]>(size_)}
    { }

    T* begin() { return data_.get(); }
    const T* begin() const { return data_.get(); }
    T* end() { return data_.get() + size_; }
    const T* end() const { return data_.get() + size_; }

  private:
    std::size_t size_;
    std::unique_ptr<T[]> data_;
};

DynArray वर्ग के उपयोगकर्ता CRTP बेस क्लास द्वारा दिए गए इंटरफेस का उपयोग आसानी से कर सकते हैं:

DynArray<int> arr(10);
arr.front() = 2;
arr[2] = 5;
assert(arr.size() == 10);

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

DynArray<int> arr(10);
DynArray<int>::Base & base = arr;
base.begin(); // no virtual calls

फंक्शन के अंदर एकमात्र स्टैटिक कास्ट begin() बेस क्लास Container<DynArray<int>> में कंपाइलर कोड को काफी ऑप्टिमाइज़ करने की अनुमति देता है और रनटाइम के दौरान कोई वर्चुअल टेबल नहीं दिखता है।

सीमाएँ: क्योंकि बेस क्लास को अलग किया जाता है और दो अलग-अलग DynArray लिए अलग-अलग है। यह संभव नहीं है कि टाइप-समरूप सरणी में अपने बेस कक्षाओं में पॉइंटर्स को स्टोर किया जा सके, क्योंकि आम तौर पर सामान्य वंशानुक्रम जहां बेस क्लास व्युत्पन्न पर निर्भर नहीं होता है प्रकार:

class A {};
class B: public A{};

A* a = new B;

कोड डुप्लिकेट से बचने के लिए CRTP

विज़िटर पैटर्न में उदाहरण CRTP के लिए एक सम्मोहक उपयोग-मामला प्रदान करता है:

struct IShape
{
    virtual ~IShape() = default;

    virtual void accept(IShapeVisitor&) const = 0;
};

struct Circle : IShape
{
    // ...        
    // Each shape has to implement this method the same way
    void accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }
    // ...
};

struct Square : IShape
{
    // ...    
    // Each shape has to implement this method the same way
    void accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }
    // ...
};

IShape प्रत्येक बच्चे के प्रकार को एक ही फ़ंक्शन को उसी तरह लागू करने की आवश्यकता होती है। यह बहुत अधिक टाइपिंग है। इसके बजाय, हम पदानुक्रम में एक नया प्रकार पेश कर सकते हैं जो हमारे लिए ऐसा करता है:

template <class Derived>
struct IShapeAcceptor : IShape {
    void accept(IShapeVisitor& visitor) const override {
        // visit with our exact type
        visitor.visit(*static_cast<Derived const*>(this));
    }
};

और अब, प्रत्येक आकृति को केवल स्वीकारकर्ता से विरासत में प्राप्त करने की आवश्यकता है:

struct Circle : IShapeAcceptor<Circle>
{
    Circle(const Point& center, double radius) : center(center), radius(radius) {}
    Point center;
    double radius;
};

struct Square : IShapeAcceptor<Square>
{
    Square(const Point& topLeft, double sideLength) : topLeft(topLeft), sideLength(sideLength) {}    
    Point topLeft;
    double sideLength;
};

कोई डुप्लिकेट कोड आवश्यक नहीं।



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow