**如何使用 std::variant 实现简易的多态结构?**

在 C++17 标准引入了 std::variant,它提供了一种类型安全的联合体,用于存储多种类型中的一种。通过 std::variant 可以轻松实现类似多态的功能,而不需要传统的继承和虚函数机制。下面通过一个完整示例,展示如何利用 std::variant 与访问器(visitor)实现一个简易的“形状”系统,并在运行时决定具体行为。


1. 设计需求

我们需要一个程序能够处理以下三种形状:

  • Circle(圆)
  • Rectangle(矩形)
  • Triangle(三角形)

每个形状都需要实现两个功能:

  1. 计算面积。
  2. 输出形状信息。

我们想在不使用虚函数的情况下完成上述需求。


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 规则

如果你想添加新形状,只需:

  1. 定义新的结构体(例如 Pentagon)。
  2. Shape std::variant 中加入该类型。
  3. ShapePrinterShapeAreaCalculator 中实现对应 operator()
  4. 编译即可,无需改动已有代码。

7. 小结

std::variant 与访问器模式是实现 C++ 类型安全多态的强大组合。它摆脱了传统继承层次的复杂性,减少了运行时开销,并保持了编译时类型检查。尤其在需要处理有限且已知的类型集合时,使用 variant 能让代码更加简洁、易读。希望本例能帮助你在项目中更灵活地利用 C++17 的新特性。

发表评论