基本原理:
CRTP(Curiously Recurring Template Pattern)是一种 C++ 编程设计模式,类似于 RAII、SFINAE、这些东西。
核心思想只有一个东西:
即派生类继承以自身为模板参数的基类模板,这样子呢,在 C++ 编译替换期间时,它可以知道模版类型信息的,所以,可以调用目标的成员函数。
不带虚函数(用途一):
#include <iostream> #include <vector> #include <cmath> #include <memory> #include <iomanip> #include <variant> // CRTP 基类模板,不继承任何虚基类,完全静态多态 template <typename Derived> class Shape { public: double area() const { return static_cast<const Derived*>(this)->calculateArea(); } double perimeter() const { return static_cast<const Derived*>(this)->calculatePerimeter(); } void printInfo() const { std::cout << std::fixed << std::setprecision(2) << "Area: " << area() << ", Perimeter: " << perimeter() << "\n"; } }; // 圆形 class Circle : public Shape<Circle> { double radius; public: Circle(double r) : radius(r) {} double calculateArea() const { return M_PI * radius * radius; } double calculatePerimeter() const { return 2 * M_PI * radius; } }; // 矩形 class Rectangle : public Shape<Rectangle> { double width, height; public: Rectangle(double w, double h) : width(w), height(h) {} double calculateArea() const { return width * height; } double calculatePerimeter() const { return 2 * (width + height); } }; // 三角形 class Triangle : public Shape<Triangle> { double a, b, c; public: Triangle(double s1, double s2, double s3) : a(s1), b(s2), c(s3) {} double calculateArea() const { double s = (a + b + c) / 2; return std::sqrt(s * (s - a) * (s - b) * (s - c)); } double calculatePerimeter() const { return a + b + c; } }; // 使用 std::variant 存储不同类型的形状,实现类型安全的统一处理 using ShapeVariant = std::variant<Circle, Rectangle, Triangle>; // Visitor 用于调用 printInfo struct PrintVisitor { template<typename T> void operator()(const T& shape) const { shape.printInfo(); } }; int main() { // 创建形状对象并存储在 std::variant 向量中 std::vector<ShapeVariant> shapes; shapes.emplace_back(Circle(5.0)); shapes.emplace_back(Rectangle(4.0, 6.0)); shapes.emplace_back(Triangle(3.0, 4.0, 5.0)); std::cout << "使用 std::variant 和 Visitor 统一处理(无虚函数调用):\n"; for (const auto& shape : shapes) { std::visit(PrintVisitor{}, shape); } // 也可以使用 lambda 直接访问 std::cout << "\n使用 lambda 直接访问 area 和 perimeter:\n"; for (const auto& shape : shapes) { std::visit([](const auto& s) { std::cout << std::fixed << std::setprecision(2) << "Area: " << s.area() << ", Perimeter: " << s.perimeter() << "\n"; }, shape); } // 静态多态单独处理示例 std::cout << "\n静态多态单独处理:\n"; Circle circle(5.0); Rectangle rectangle(4.0, 6.0); Triangle triangle(3.0, 4.0, 5.0); circle.printInfo(); rectangle.printInfo(); triangle.printInfo(); return 0; }以下是带基类虚函数(用途二):
C++ 17
#include <iostream> #include <vector> #include <cmath> #include <variant> #include <memory> #include <iomanip> // CRTP 基类模板 template <typename Derived> class Shape { public: double area() const { return static_cast<const Derived*>(this)->calculateArea(); } double perimeter() const { return static_cast<const Derived*>(this)->calculatePerimeter(); } void printInfo() const { std::cout << std::fixed << std::setprecision(2) << "Area: " << area() << ", Perimeter: " << perimeter() << "\n"; } }; // 圆形 class Circle : public Shape<Circle> { double radius; public: Circle(double r) : radius(r) {} double calculateArea() const { return M_PI * radius * radius; } double calculatePerimeter() const { return 2 * M_PI * radius; } }; // 矩形 class Rectangle : public Shape<Rectangle> { double width, height; public: Rectangle(double w, double h) : width(w), height(h) {} double calculateArea() const { return width * height; } double calculatePerimeter() const { return 2 * (width + height); } }; // 三角形 class Triangle : public Shape<Triangle> { double a, b, c; public: Triangle(double s1, double s2, double s3) : a(s1), b(s2), c(s3) {} double calculateArea() const { double s = (a + b + c) / 2; return sqrt(s * (s - a) * (s - b) * (s - c)); } double calculatePerimeter() const { return a + b + c; } }; // 使用 std::variant 存储不同类型的形状 using ShapeVariant = std::variant<Circle, Rectangle, Triangle>; // 访问者类,用于调用 variant 中的对象的成员函数 class ShapeVisitor { public: void operator()(const Circle& c) const { std::cout << "Circle: "; c.printInfo(); } void operator()(const Rectangle& r) const { std::cout << "Rectangle: "; r.printInfo(); } void operator()(const Triangle& t) const { std::cout << "Triangle: "; t.printInfo(); } }; int main() { // 创建不同类型的图形对象 Circle circle(5.0); Rectangle rectangle(4.0, 6.0); Triangle triangle(3.0, 4.0, 5.0); std::cout << "单独处理每个形状:\n"; circle.printInfo(); rectangle.printInfo(); triangle.printInfo(); std::cout << "\n使用 std::variant 统一处理:\n"; // 创建 variant 的 vector std::vector<ShapeVariant> shapes; shapes.push_back(circle); shapes.push_back(rectangle); shapes.push_back(triangle); ShapeVisitor visitor; for (const auto& shape : shapes) { std::visit(visitor, shape); } std::cout << "\n使用 lambda 表达式处理 variant:\n"; for (const auto& shape : shapes) { std::visit([](const auto& s) { using T = std::decay_t<decltype(s)>; if constexpr (std::is_same_v<T, Circle>) { std::cout << "Circle: "; } else if constexpr (std::is_same_v<T, Rectangle>) { std::cout << "Rectangle: "; } else if constexpr (std::is_same_v<T, Triangle>) { std::cout << "Triangle: "; } s.printInfo(); }, shape); } return 0; }C++ 11
#include <iostream> #include <vector> #include <cmath> #include <memory> #include <iomanip> // 非模板基类,用于类型擦除 class IShape { public: virtual double area() const = 0; virtual double perimeter() const = 0; virtual void printInfo() const = 0; virtual ~IShape() = default; }; // CRTP 基类模板 template <typename Derived> class Shape : public IShape { public: double area() const override { return static_cast<const Derived*>(this)->calculateArea(); } double perimeter() const override { return static_cast<const Derived*>(this)->calculatePerimeter(); } void printInfo() const override { std::cout << std::fixed << std::setprecision(2) << "Area: " << area() << ", Perimeter: " << perimeter() << "\n"; } }; // 圆形 class Circle : public Shape<Circle> { double radius; public: Circle(double r) : radius(r) {} double calculateArea() const { return M_PI * radius * radius; } double calculatePerimeter() const { return 2 * M_PI * radius; } }; // 矩形 class Rectangle : public Shape<Rectangle> { double width, height; public: Rectangle(double w, double h) : width(w), height(h) {} double calculateArea() const { return width * height; } double calculatePerimeter() const { return 2 * (width + height); } }; // 三角形 class Triangle : public Shape<Triangle> { double a, b, c; public: Triangle(double s1, double s2, double s3) : a(s1), b(s2), c(s3) {} double calculateArea() const { double s = (a + b + c) / 2; return sqrt(s * (s - a) * (s - b) * (s - c)); } double calculatePerimeter() const { return a + b + c; } }; int main() { // 创建不同类型的图形对象 std::cout << "使用基类指针统一处理:\n"; // 使用基类指针的 vector std::vector<std::unique_ptr<IShape>> shapes; shapes.push_back(std::make_unique<Circle>(5.0)); shapes.push_back(std::make_unique<Rectangle>(4.0, 6.0)); shapes.push_back(std::make_unique<Triangle>(3.0, 4.0, 5.0)); for (const auto& shape : shapes) { shape->printInfo(); } // 单独处理每个形状(静态多态) std::cout << "\n使用静态多态单独处理:\n"; Circle circle(5.0); Rectangle rectangle(4.0, 6.0); Triangle triangle(3.0, 4.0, 5.0); circle.printInfo(); rectangle.printInfo(); triangle.printInfo(); return 0; }