在 C++17 标准引入了 std::variant,它提供了一种类型安全的联合体,用于存储多种类型中的一种。通过 std::variant 可以轻松实现类似多态的功能,而不需要传统的继承和虚函数机制。下面通过一个完整示例,展示如何利用 std::variant 与访问器(visitor)实现一个简易的“形状”系统,并在运行时决定具体行为。
1. 设计需求
我们需要一个程序能够处理以下三种形状:
Circle(圆)Rectangle(矩形)Triangle(三角形)
每个形状都需要实现两个功能:
- 计算面积。
- 输出形状信息。
我们想在不使用虚函数的情况下完成上述需求。
2. 结构体定义
#include <iostream>
#include <variant>
#include <cmath>
struct Circle {
double radius;
double area() const { return M_PI * radius * radius; }
void print() const { std::cout << "Circle(radius=" << radius << ")\n"; }
};
struct Rectangle {
double width, height;
double area() const { return width * height; }
void print() const { std::cout << "Rectangle(w=" << width << ", h=" << height << ")\n"; }
};
struct Triangle {
double base, height;
double area() const { return 0.5 * base * height; }
void print() const { std::cout << "Triangle(b=" << base << ", h=" << height << ")\n"; }
};
3. 定义 std::variant
using Shape = std::variant<Circle, Rectangle, Triangle>;
现在 Shape 可以持有三种形状中的任意一种。
4. 访问器(Visitor)
使用结构体重载 operator() 为每种形状实现对应的操作。
struct ShapePrinter {
void operator()(const Circle& c) const { c.print(); }
void operator()(const Rectangle& r) const { r.print(); }
void operator()(const Triangle& t) const { t.print(); }
};
struct ShapeAreaCalculator {
double operator()(const Circle& c) const { return c.area(); }
double operator()(const Rectangle& r) const { return r.area(); }
double operator()(const Triangle& t) const { return t.area(); }
};
5. 主程序演示
int main() {
Shape shapes[] = {
Circle{5.0},
Rectangle{4.0, 6.0},
Triangle{3.0, 7.0}
};
for (const auto& s : shapes) {
// 打印信息
std::visit(ShapePrinter{}, s);
// 计算面积
double a = std::visit(ShapeAreaCalculator{}, s);
std::cout << "Area = " << a << "\n\n";
}
return 0;
}
运行结果:
Circle(radius=5)
Area = 78.5398
Rectangle(w=4, h=6)
Area = 24
Triangle(b=3, h=7)
Area = 10.5
6. 优点与扩展
| 传统虚函数 | std::variant + visitor |
|---|---|
| 需要继承层次 | 仅使用 POD 结构体 |
| 运行时多态 | 类型安全,编译时检查 |
| 维护成本高 | 更易于扩展,添加新形状只需新增结构体和 visitor 规则 |
如果你想添加新形状,只需:
- 定义新的结构体(例如
Pentagon)。 - 在
Shapestd::variant中加入该类型。 - 在
ShapePrinter与ShapeAreaCalculator中实现对应operator()。 - 编译即可,无需改动已有代码。
7. 小结
std::variant 与访问器模式是实现 C++ 类型安全多态的强大组合。它摆脱了传统继承层次的复杂性,减少了运行时开销,并保持了编译时类型检查。尤其在需要处理有限且已知的类型集合时,使用 variant 能让代码更加简洁、易读。希望本例能帮助你在项目中更灵活地利用 C++17 的新特性。