You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中实现带关联附加数据的枚举(类似SML/Haskell类型)

问题描述

我希望创建一个返回枚举值的函数,但针对特定枚举值时需附带额外数据。在SML中,我会将返回类型定义为:

datatype rettype = A | B | C of data | D of data

其中data是某个预先定义的类型。

在C++中,我目前的实现方式如下:

enum class AuxEnum { A, B, C, D };
using RetType = std::pair<AuxEnum, std::optional<Data>>;

该方案虽能运行,但我困扰于枚举值与是否提供附加数据之间无实际关联。请问是否有更优的实现方案?

更优实现方案

方案一:使用std::variant(C++17及以上)

这是最接近SML代数数据类型的实现,能强制约束枚举值和附加数据的关联,彻底避免A带数据、C不带数据这类非法情况:

#include <variant>

// 用空结构体标记无附加数据的情况
struct TagA {};
struct TagB {};

// 带数据的类型直接封装Data
struct TagC { Data data; };
struct TagD { Data data; };

// 最终返回类型:只能是四种合法情况之一
using RetType = std::variant<TagA, TagB, TagC, TagD>;

使用时通过std::visit处理不同分支,全程类型安全:

void handleResult(const RetType& result) {
    std::visit([](auto&& arg) {
        using ArgType = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<ArgType, TagA>) {
            // 处理A的逻辑
        } else if constexpr (std::is_same_v<ArgType, TagB>) {
            // 处理B的逻辑
        } else if constexpr (std::is_same_v<ArgType, TagC> || std::is_same_v<ArgType, TagD>) {
            // 处理带Data的逻辑,直接访问arg.data
        }
    }, result);
}

方案二:使用多态继承体系

如果需要让不同返回值自带处理逻辑,可采用基类+派生类的方式:

#include <memory>

// 抽象基类,定义统一处理接口
class RetType {
public:
    virtual ~RetType() = default;
    virtual void handle() const = 0;
};

// 无数据的派生类
class RetA : public RetType {
public:
    void handle() const override {
        // 实现A的处理逻辑
    }
};

class RetB : public RetType {
public:
    void handle() const override {
        // 实现B的处理逻辑
    }
};

// 带数据的派生类
class RetC : public RetType {
public:
    Data data;
    explicit RetC(Data d) : data(std::move(d)) {}
    void handle() const override {
        // 用this->data实现C的处理逻辑
    }
};

class RetD : public RetType {
public:
    Data data;
    explicit RetD(Data d) : data(std::move(d)) {}
    void handle() const override {
        // 用this->data实现D的处理逻辑
    }
};

返回时使用std::unique_ptr<RetType>,调用方直接调用handle()即可。缺点是需要动态分配内存,性能略低于std::variant。

方案三:带标签的简化变体(不推荐复杂场景)

如果嫌定义多个结构体麻烦,可结合枚举标签和std::variant,但需要手动保证标签与数据的匹配:

#include <variant>
#include <utility>

enum class AuxEnum { A, B, C, D };
using RetType = std::pair<AuxEnum, std::variant<std::monostate, Data>>;

这种方式仍存在非法组合的可能,仅适合简单场景。

方案对比

  • std::variant:类型安全、无动态内存开销,完美对应SML的代数数据类型,优先推荐。
  • 多态继承:灵活性高,适合需要自定义处理逻辑的场景,但有性能损耗。
  • 带标签的变体:实现简单但安全性不足,复杂逻辑中不建议使用。

内容的提问来源于stack exchange,提问作者vukung

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 16:01:09