কোনও বইয়ের উল্লেখ না করে, কেউ দয়া করে CRTP
কোনও কোড উদাহরণ সহ একটি ভাল ব্যাখ্যা সরবরাহ করতে পারেন ?
কোনও বইয়ের উল্লেখ না করে, কেউ দয়া করে CRTP
কোনও কোড উদাহরণ সহ একটি ভাল ব্যাখ্যা সরবরাহ করতে পারেন ?
উত্তর:
সংক্ষেপে, সিআরটিপি হয় যখন কোনও ক্লাসের A
একটি বেস ক্লাস থাকে যা ক্লাসের জন্য A
নিজেই একটি টেম্পলেট বিশেষীকরণ । যেমন
template <class T>
class X{...};
class A : public X<A> {...};
এটা তোলে হয় অদ্ভুতভাবে, আবর্তক তাই না? :)
এখন, এটি আপনাকে কী দেয়? এটি প্রকৃতপক্ষে X
টেমপ্লেটটিকে তার বিশেষত্বের জন্য বেস বর্গ হওয়ার ক্ষমতা দেয় ।
উদাহরণস্বরূপ, আপনি এর মতো জেনেরিক সিঙ্গলটন ক্লাস (সরলীকৃত সংস্করণ) তৈরি করতে পারেন
template <class ActualClass>
class Singleton
{
public:
static ActualClass& GetInstance()
{
if(p == nullptr)
p = new ActualClass;
return *p;
}
protected:
static ActualClass* p;
private:
Singleton(){}
Singleton(Singleton const &);
Singleton& operator = (Singleton const &);
};
template <class T>
T* Singleton<T>::p = nullptr;
এখন, একটি স্বেচ্ছাসেবক শ্রেণিকে A
একটি সিঙ্গলটন করার জন্য আপনার এটি করা উচিত
class A: public Singleton<A>
{
//Rest of functionality for class A
};
তাহলে তুমি দেখ? সিঙ্গেলটন টেমপ্লেট ধরে নিয়েছে যে কোনও ধরণের জন্য এর বিশেষীকরণটি X
উত্তরাধিকার সূত্রে প্রাপ্ত singleton<X>
হবে এবং সুতরাং এর সাথে তার সমস্ত (জনসাধারণ, সুরক্ষিত) সদস্য অ্যাক্সেসযোগ্য থাকবে GetInstance
! সিআরটিপি এর অন্যান্য দরকারী ব্যবহার রয়েছে। উদাহরণস্বরূপ, আপনি যদি বর্তমানে আপনার শ্রেণীর জন্য বিদ্যমান সমস্ত উদাহরণ গণনা করতে চান তবে এই যুক্তিটিকে একটি পৃথক টেম্পলেটে আবদ্ধ করতে চান (একটি কংক্রিট বর্গের ধারণাটি বেশ সহজ - একটি স্ট্যাটিক ভেরিয়েবল, সিটারে বর্ধন, ডিটারে হ্রাস) )। অনুশীলন হিসাবে এটি করার চেষ্টা করুন!
তবুও আরেকটি দরকারী উদাহরণ, বুস্টের জন্য (তারা কীভাবে এটি প্রয়োগ করেছে তা আমি নিশ্চিত নই, তবে সিআরটিপিও তা করবে) do কল্পনা করুন আপনি <
নিজের ক্লাসের জন্য কেবল অপারেটর সরবরাহ করতে চান তবে ==
তাদের জন্য স্বয়ংক্রিয়ভাবে অপারেটর !
আপনি এটি এইভাবে করতে পারে:
template<class Derived>
class Equality
{
};
template <class Derived>
bool operator == (Equality<Derived> const& op1, Equality<Derived> const & op2)
{
Derived const& d1 = static_cast<Derived const&>(op1);//you assume this works
//because you know that the dynamic type will actually be your template parameter.
//wonderful, isn't it?
Derived const& d2 = static_cast<Derived const&>(op2);
return !(d1 < d2) && !(d2 < d1);//assuming derived has operator <
}
এখন আপনি এটি এর মতো ব্যবহার করতে পারেন
struct Apple:public Equality<Apple>
{
int size;
};
bool operator < (Apple const & a1, Apple const& a2)
{
return a1.size < a2.size;
}
এখন, আপনি স্পষ্টভাবে অপারেটর প্রদান করেন নি ==
জন্য Apple
? তবে আপনি এটি আছে! তুমি লিখতে পারো
int main()
{
Apple a1;
Apple a2;
a1.size = 10;
a2.size = 10;
if(a1 == a2) //the compiler won't complain!
{
}
}
এই মনে করতে পারে যে আপনি কম লিখতে হবে যদি আপনি শুধু অপারেটর লিখেছিলেন ==
জন্য Apple
, কিন্তু কল্পনা Equality
টেমপ্লেট না শুধুমাত্র প্রদান করবে ==
কিন্তু >
, >=
, <=
ইত্যাদি আপনার জন্য এই সংজ্ঞা ব্যবহার করতে পারে একাধিক ক্লাস, কোড পুনঃব্যবহার!
সিআরটিপি একটি দুর্দান্ত জিনিস :) এইচটিএইচ
এখানে আপনি একটি দুর্দান্ত উদাহরণ দেখতে পারেন। আপনি যদি ভার্চুয়াল পদ্ধতি ব্যবহার করেন তবে প্রোগ্রামটি জানবে যে রানটাইমে কী কার্যকর হয়। সংকলক সিআরটিপি বাস্তবায়ন করছে যা সংকলনের সময় সিদ্ধান্ত নেয় !!! এটি দুর্দান্ত পারফরম্যান্স!
template <class T>
class Writer
{
public:
Writer() { }
~Writer() { }
void write(const char* str) const
{
static_cast<const T*>(this)->writeImpl(str); //here the magic is!!!
}
};
class FileWriter : public Writer<FileWriter>
{
public:
FileWriter(FILE* aFile) { mFile = aFile; }
~FileWriter() { fclose(mFile); }
//here comes the implementation of the write method on the subclass
void writeImpl(const char* str) const
{
fprintf(mFile, "%s\n", str);
}
private:
FILE* mFile;
};
class ConsoleWriter : public Writer<ConsoleWriter>
{
public:
ConsoleWriter() { }
~ConsoleWriter() { }
void writeImpl(const char* str) const
{
printf("%s\n", str);
}
};
virtual void write(const char* str) const = 0;
? যদিও তা ন্যায্য, অন্য কৌশলগুলি করার সময় এই কৌশলটি অত্যন্ত সহায়ক বলে মনে write
হয়।
সিআরটিপি হ'ল সংকলন-সময় বহুবচন প্রয়োগ করার একটি কৌশল। এখানে একটি খুব সাধারণ উদাহরণ। নীচের উদাহরণে, শ্রেণি ইন্টারফেসের ProcessFoo()
সাথে কাজ করছে Base
এবং Base::Foo
উত্পন্ন বস্তুর foo()
পদ্ধতিটি আহ্বান জানায় , যা ভার্চুয়াল পদ্ধতিগুলির সাথে আপনি কী করতে চান।
http://coliru.stacked-crooked.com/a/2d27f1e09d567d0e
template <typename T>
struct Base {
void foo() {
(static_cast<T*>(this))->foo();
}
};
struct Derived : public Base<Derived> {
void foo() {
cout << "derived foo" << endl;
}
};
struct AnotherDerived : public Base<AnotherDerived> {
void foo() {
cout << "AnotherDerived foo" << endl;
}
};
template<typename T>
void ProcessFoo(Base<T>* b) {
b->foo();
}
int main()
{
Derived d1;
AnotherDerived d2;
ProcessFoo(&d1);
ProcessFoo(&d2);
return 0;
}
আউটপুট:
derived foo
AnotherDerived foo
foo()
যা উদ্ভূত শ্রেণীর দ্বারা প্রয়োগ করা হয়।
ProcessFoo()
ফাংশনের সাথে কেন কার্যকর।
void ProcessFoo(T* b)
কোডটির বিন্দুটি পাই না, কারণ ডেরিভড এবং অ্যান্ডেল ডাইরাইভের সাথে এবং না করেই বাস্তবে প্রাপ্ত এটি এখনও কার্যকর হবে। IMHO এটি আরও আকর্ষণীয় হবে যদি প্রসেসফু কোনওভাবে টেম্পলেটগুলির ব্যবহার না করে।
ProcessFoo()
যে কোনও প্রকারের সাথে কাজ করবে যা ইন্টারফেস প্রয়োগ করে অর্থাত্ এই ক্ষেত্রে ইনপুট টাইপ টি নামক একটি পদ্ধতি থাকা উচিত foo()
। দ্বিতীয়ত, ProcessFoo
একাধিক প্রকারের সাথে কাজ করার জন্য অ-পরীক্ষামূলকভাবে পেতে, আপনি সম্ভবত আরটিটিআই ব্যবহার করবেন যা আমরা এড়াতে চাই। তদ্ব্যতীত, টেমপ্ল্লেটাইজড সংস্করণ আপনাকে ইন্টারফেসে সংকলন সময় পরীক্ষা করে থাকে।
এটি সরাসরি উত্তর নয়, বরং সিআরটিপি কীভাবে কার্যকর হতে পারে তার একটি উদাহরণ ।
একটি ভাল কংক্রিট উদাহরণ CRTP হয় std::enable_shared_from_this
সি ++ 11 থেকে:
একটি শ্রেণি মেম্বার ফাংশনগুলির উত্তরাধিকারী
T
হতে উত্তরাধিকারী হতেenable_shared_from_this<T>
পারেshared_from_this
যা একটিshared_ptr
উদাহরণ দেখায়*this
।
এটি হ'ল উত্তরাধিকার সূত্রে প্রাপ্ত হ'ল std::enable_shared_from_this
আপনার উদাহরণটিতে অ্যাক্সেস না করে ভাগ করা (বা দুর্বল) পয়েন্টার পাওয়া সম্ভব করে তোলে (যেমন কোনও সদস্য ফাংশন থেকে যেখানে আপনি কেবল জানেন *this
)।
এটি কার্যকর যখন আপনি দিতে হয় std::shared_ptr
তবে আপনার কেবল এতে অ্যাক্সেস থাকে *this
:
struct Node;
void process_node(const std::shared_ptr<Node> &);
struct Node : std::enable_shared_from_this<Node> // CRTP
{
std::weak_ptr<Node> parent;
std::vector<std::shared_ptr<Node>> children;
void add_child(std::shared_ptr<Node> child)
{
process_node(shared_from_this()); // Shouldn't pass `this` directly.
child->parent = weak_from_this(); // Ditto.
children.push_back(std::move(child));
}
};
আপনি কেবল this
পরিবর্তে সরাসরি পাস করতে পারবেন না কারণ shared_from_this()
এটি মালিকানা প্রক্রিয়াটি ভেঙে দেবে:
struct S
{
std::shared_ptr<S> get_shared() const { return std::shared_ptr<S>(this); }
};
// Both shared_ptr think they're the only owner of S.
// This invokes UB (double-free).
std::shared_ptr<S> s1 = std::make_shared<S>();
std::shared_ptr<S> s2 = s1->get_shared();
assert(s2.use_count() == 1);
ঠিক যেমন নোট:
স্ট্যাটিক পলিমারফিজম (যা ডায়নামিক পলিমারফিজম পছন্দ করে তবে ভার্চুয়াল ফাংশন পয়েন্টার টেবিল ছাড়াই) সিআরটিপি ব্যবহার করতে পারে could
#pragma once
#include <iostream>
template <typename T>
class Base
{
public:
void method() {
static_cast<T*>(this)->method();
}
};
class Derived1 : public Base<Derived1>
{
public:
void method() {
std::cout << "Derived1 method" << std::endl;
}
};
class Derived2 : public Base<Derived2>
{
public:
void method() {
std::cout << "Derived2 method" << std::endl;
}
};
#include "crtp.h"
int main()
{
Derived1 d1;
Derived2 d2;
d1.method();
d2.method();
return 0;
}
আউটপুটটি হবে:
Derived1 method
Derived2 method
vtable
সিআরটিপি ব্যবহার না করেই করা যেতে পারে । vtable
সত্যিকার অর্থে কী সরবরাহ করা হয় তা হ'ল উদ্ভূত পদ্ধতিগুলিকে কল করতে বেস ক্লাস (পয়েন্টার বা রেফারেন্স) ব্যবহার করা। এটি এখানে সিআরটিপি দিয়ে কীভাবে করা হচ্ছে তা আপনার দেখা উচিত।
Base<>::method ()
এমনকি বলা হয় না, বা আপনি কোথাও বহুরূপী ব্যবহার করেন না।
methodImpl
মধ্যে method
এর Base
এবং উদ্ভূত ক্লাসের নাম methodImpl
পরিবর্তেmethod