สำนวน CRTP
รูปแบบเทมเพลตแบบวนกลับอย่างน่าพิศวง
สำนวน CRTP เป็นบทเรียน C++ Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C++ Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C++ Academy มีบทเรียนทั้งหมด 4 บทเรียน
CRTP คืออะไร
รูปแบบแม่แบบที่เกิดซ้ำอย่างน่าประหลาด (CRTP) เป็นสำนวนการเขียน C++ ที่คลาส Derived สืบทอดจากคลาสพื้นฐานแม่แบบที่สร้างอินสแตนซ์ด้วยตัวมันเองซึ่งก็คือ Derived
- รูปแบบ:
class Derived : public Base<Derived> - คลาสพื้นฐานจะทราบชนิดที่สืบทอดขณะคอมไพล์
template <typename T>
class Base {};
class Derived : public Base<Derived> {};กลไกหลัก
ภายในคลาสพื้นฐาน คุณสามารถใช้ static_cast แปลงพอยน์เตอร์ this เป็นชนิดที่สืบทอดได้ เนื่องจากชนิดที่สืบทอดเป็นพารามิเตอร์แม่แบบ การแปลงนี้จึงเกิดขึ้นทั้งหมดขณะคอมไพล์และไม่มีต้นทุนขณะทำงาน
#include <iostream>
template <typename T>
class Base {
public:
void interface() {
static_cast<T*>(this)->implementation();
}
};
class Derived : public Base<Derived> {
public:
void implementation() { std::cout << "Derived impl\n"; }
};
int main() {
Derived d;
d.interface();
return 0;
}ทำไมจึงเรียกว่า "เกิดซ้ำอย่างน่าประหลาด"
ชื่อนี้มาจากความขัดแย้งที่ดูเหมือนจะเกิดขึ้น นั่นคือคลาสพื้นฐานอ้างถึงคลาสที่สืบทอด ทั้งที่คลาสนั้นยังไม่ได้ถูกกำหนดอย่างสมบูรณ์เมื่อเขียนแม่แบบคลาสพื้นฐาน
- รูปแบบนี้ทำงานได้เพราะแม่แบบจะถูกสร้างอินสแตนซ์เมื่อมีการใช้งานเท่านั้น
- เมื่อถึงเวลาสร้างอินสแตนซ์
Derivedจะเป็นชนิดที่สมบูรณ์แล้ว
การใช้งานจริงครั้งแรก
CRTP ช่วยให้คลาสพื้นฐานจัดเตรียมฟังก์ชันที่นำกลับมาใช้ใหม่ได้ โดยเรียกกลับไปยังคลาสที่สืบทอด ในที่นี้คลาสพื้นฐานจัดเตรียม print() ซึ่งส่งต่อการทำงานไปยัง name() ของคลาสที่สืบทอด
#include <iostream>
#include <string>
template <typename T>
struct Named {
void print() {
std::cout << static_cast<T*>(this)->name() << "\n";
}
};
struct Cat : Named<Cat> {
std::string name() { return "Cat"; }
};
int main() {
Cat c;
c.print();
return 0;
}ไม่มีตารางเสมือน
ต่างจากพหุรูปแบบขณะทำงาน CRTP ไม่ต้องใช้คีย์เวิร์ด virtual และไม่ต้องมีพอยน์เตอร์ตารางเสมือนในแต่ละออบเจกต์ การเลือกเรียกจะได้รับการแก้ไขทั้งหมดขณะคอมไพล์
- ออบเจกต์มีขนาดเล็กลง (ไม่มี vptr ที่ซ่อนอยู่)
- การเรียกสามารถแทรกโค้ดได้
การนับอินสแตนซ์
การประยุกต์ใช้ CRTP แบบคลาสสิกคือการนับออบเจกต์ คลาสที่สืบทอดแต่ละคลาสจะได้รับตัวนับแบบสแตติกของตนเองโดยอัตโนมัติ
#include <iostream>
template <typename T>
struct Counter {
static inline int count = 0;
Counter() { ++count; }
~Counter() { --count; }
};
struct Widget : Counter<Widget> {};
int main() {
Widget a, b;
std::cout << Widget::count << "\n";
return 0;
}คลาสที่สืบทอดแต่ละคลาสมีสถานะของตนเอง
เนื่องจาก Counter<Widget> และ Counter<Gadget> เป็นอินสแตนซ์ของแม่แบบที่แตกต่างกัน แต่ละรายการจึงมี count แบบสแตติกที่เป็นอิสระต่อกัน การแยกเช่นนี้ไม่สามารถทำได้ด้วยคลาสพื้นฐานที่ไม่ใช่แม่แบบเพียงคลาสเดียว
#include <iostream>
template <typename T>
struct Counter {
static inline int count = 0;
Counter() { ++count; }
};
struct A : Counter<A> {};
struct B : Counter<B> {};
int main() {
A a1, a2;
B b1;
std::cout << A::count << " " << B::count << "\n";
return 0;
}static_cast ปลอดภัยในกรณีนี้
การแปลงชนิดลงภายในคลาสพื้นฐานจะปลอดภัยก็ต่อเมื่อออบเจกต์ทุกตัวของชนิด Base<T> เป็น T จริง ๆ เท่านั้น CRTP รับประกันสิ่งนี้ตั้งแต่โครงสร้าง เพราะ T คือคลาสที่สืบทอด
- อย่าสร้างอินสแตนซ์
Base<Wrong>ให้กับคลาสที่ไม่เกี่ยวข้อง
ตัวดำเนินการเปรียบเทียบผ่าน CRTP
คุณสามารถสร้างชุดตัวดำเนินการเปรียบเทียบครบชุดจาก operator< เพียงรายการเดียว โดยสืบทอดจากตัวช่วยด้านการเปรียบเทียบ
#include <iostream>
template <typename T>
struct Comparable {
friend bool operator>(const T& a, const T& b) { return b < a; }
friend bool operator==(const T& a, const T& b) { return !(a < b) && !(b < a); }
};
struct Num : Comparable<Num> {
int v;
Num(int x) : v(x) {}
friend bool operator<(const Num& a, const Num& b) { return a.v < b.v; }
};
int main() {
std::cout << (Num(3) > Num(2)) << "\n";
return 0;
}การป้องกันด้วยตัวสร้างแบบ protected
เพื่อป้องกันการใช้คลาสพื้นฐานผิดโดยไม่ตั้งใจ ให้คลาสพื้นฐานมีตัวสร้างแบบ protected เพื่อให้มีเพียงคลาสที่สืบทอดเท่านั้นที่สร้างคลาสนี้ได้
#include <iostream>
template <typename T>
class Base {
protected:
Base() = default;
public:
void run() { static_cast<T*>(this)->step(); }
};
class Job : public Base<Job> {
public:
void step() { std::cout << "step\n"; }
};
int main() {
Job j;
j.run();
return 0;
}CRTP ในไลบรารีมาตรฐาน
พบ CRTP อยู่ทั่วทั้งไลบรารี C++ ที่ใช้งานจริง
std::enable_shared_from_this<T>ใช้รูปแบบนี้- ไลบรารีคณิตศาสตร์ที่ใช้แม่แบบนิพจน์จำนวนมาก (Eigen) อาศัยรูปแบบนี้
- ตัวช่วยสำหรับช่วงและตัววนซ้ำก็ใช้รูปแบบที่คล้ายกัน
การจดจำรูปแบบ X : Base<X> ช่วยให้คุณอ่านโค้ดขั้นสูงได้
ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจของคุณเกี่ยวกับรูปแบบ CRTP
สรุปทบทวน
คุณได้เรียนรู้สำนวนการเขียน CRTP
Derived : Base<Derived>ทำให้คลาสพื้นฐานทราบชนิดรูปธรรมของตนstatic_cast<T*>(this)ช่วยให้เลือกเรียกขณะคอมไพล์- ไม่มีตารางเสมือน ออบเจกต์มีขนาดเล็กลง และการเรียกสามารถแทรกโค้ดได้
- ใช้กับตัวนับ ส่วนผสมสำหรับการเปรียบเทียบ และตัวช่วยในไลบรารี
คำถามที่พบบ่อย
บทเรียน “สำนวน CRTP” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “สำนวน CRTP” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C++ Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C++ Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “สำนวน CRTP”
รูปแบบเทมเพลตแบบวนกลับอย่างน่าพิศวง คุณปฏิบัติ C++ Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C++ Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C++ Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “สำนวน CRTP” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C++ Academy นี้ได้ไหม
ได้ บทเรียน C++ Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ