1. 实现 drawShapes
函数
#include <iostream>
// 圆形类
class Circle {
public:
Circle(float radius) : radius(radius) {}
void draw() const {
std::cout << "Drawing Circle with radius " << radius << std::endl;
}
private:
float radius;
};
// 矩形类
class Rectangle {
public:
Rectangle(float width, float height) : width(width), height(height) {}
void draw() const {
std::cout << "Drawing Rectangle with width " << width << " and height " << height << std::endl;
}
private:
float width;
float height;
};
// 可变参数模板函数
template<typename... Shapes>
void drawShapes(const Shapes&... shapes) {
(shapes.draw(), ...);
}
2. 编译期类型检查
- 基于概念(C++20 及以后):可以定义概念(Concepts)来约束传入
drawShapes
函数的类型必须具有 draw
成员函数。
template<typename T>
concept Drawable = requires(T t) {
{ t.draw() } -> std::same_as<void>;
};
template<Drawable... Shapes>
void drawShapes(const Shapes&... shapes) {
(shapes.draw(), ...);
}
- SFINAE(C++11 - C++17):利用 Substitution Failure Is Not An Error(SFINAE)原则,在编译期判断类型是否具有
draw
成员函数。
#include <type_traits>
template<typename T, typename = void>
struct has_draw_method : std::false_type {};
template<typename T>
struct has_draw_method<T, std::void_t<decltype(std::declval<T>().draw())>> : std::true_type {};
template<typename... Shapes, typename = std::enable_if_t<(has_draw_method<Shapes>::value && ...)>>
void drawShapes(const Shapes&... shapes) {
(shapes.draw(), ...);
}
3. 编译期优化
- 常量折叠:如果某些计算在编译期就可以确定结果,编译器会进行常量折叠。例如,在计算几何形状的某些属性时,如果参数是编译期常量,相关计算会在编译期完成。
- 死代码消除:编译器会消除不会执行到的代码。在
drawShapes
函数中,如果某个类型检查失败,对应的代码路径不会被编译,从而减少可执行文件大小。
- 内联:现代编译器通常会对短小的函数(如
draw
函数)进行内联优化,减少函数调用开销。在 drawShapes
函数中,由于直接调用 draw
函数,编译器可以更好地进行内联优化。
4. 通用性和扩展性
- 通用性:通过可变参数模板,
drawShapes
函数可以接受任意数量、任意符合条件(具有 draw
函数)的类型。这使得函数可以处理各种不同类型的几何形状,甚至可以处理未来新增的形状类,只要它们具有 draw
函数。
- 扩展性:当需要新增一种几何形状时,只需要定义新的形状类并提供
draw
成员函数,无需修改 drawShapes
函数的实现。如果使用概念,新形状类自然要满足 Drawable
概念;如果使用 SFINAE,新形状类只要满足 has_draw_method
检查即可。