如何编写可容纳不同强类型枚举错误码的C++错误结构体?
保留错误码类型信息的通用错误结构体实现
这确实是个很实际的需求——把强类型枚举直接转成int存储,虽然简单,但会丢失类型上下文,后续做错误匹配、日志输出或者调试时都会很不方便。下面给你两种靠谱的实现思路,你可以根据自己的场景选择:
方案一:用std::variant存储多类型错误码(推荐)
C++17引入的std::variant天生适合这种需要存储多种类型但保留原始类型信息的场景。我们可以把错误码定义为一个能容纳int和各种强枚举的variant,同时搭配错误消息:
#include <string> #include <variant> #include <stdexcept> // 你的强类型枚举示例 enum class NetworkErrorCode { OK = 0, CONNECTION_FAILURE, EMPTY_RESPONSE, HOST_RESOLUTION_FAILURE, UNKNOWN_ERROR = 1000 }; struct Error { // 可扩展:添加更多需要支持的错误码类型到variant模板参数里 std::variant<int, NetworkErrorCode> code; std::string message; // 模板构造函数,接受任意支持的错误码类型和消息 template<typename CodeType> Error(CodeType err_code, std::string msg) : code(std::move(err_code)), message(std::move(msg)) { // 可选:验证CodeType是否是我们支持的类型 if (!std::holds_alternative<CodeType>(code)) { throw std::invalid_argument("Unsupported error code type"); } } // 工具方法:判断当前错误码是否是指定类型 template<typename CodeType> bool is() const { return std::holds_alternative<CodeType>(code); } // 工具方法:获取指定类型的错误码(如果类型不匹配会抛出异常) template<typename CodeType> const CodeType& get() const { return std::get<CodeType>(code); } };
使用示例
// 创建int类型的错误 Error int_error(404, "Resource not found"); // 创建网络枚举类型的错误 Error net_error(NetworkErrorCode::CONNECTION_FAILURE, "Failed to connect to server"); // 判断错误类型并处理 if (net_error.is<NetworkErrorCode>()) { auto net_code = net_error.get<NetworkErrorCode>(); if (net_code == NetworkErrorCode::CONNECTION_FAILURE) { // 处理连接失败逻辑 } } // 如果尝试获取错误类型,会抛出std::bad_variant_access异常 // int_error.get<NetworkErrorCode>(); // 这行代码会报错
这种方案的好处是灵活易扩展,需要新增错误码类型时,只需要给variant添加新的模板参数即可,不需要修改构造函数或其他逻辑。
方案二:模板结构体+特化(适合需要定制行为的场景)
如果你需要针对不同的错误码类型实现不同的逻辑(比如不同的序列化方式、错误处理逻辑),可以用模板结构体结合特化:
#include <string> enum class NetworkErrorCode { OK = 0, CONNECTION_FAILURE, EMPTY_RESPONSE, HOST_RESOLUTION_FAILURE, UNKNOWN_ERROR = 1000 }; // 基础模板声明 template<typename CodeType> struct Error { CodeType code; std::string message; Error(CodeType c, std::string msg) : code(c), message(std::move(msg)) {} // 可以在这里定义通用方法,或者留空让特化版本实现 std::string to_string() const; }; // int类型的特化(如果需要定制逻辑) template<> struct Error<int> { int code; std::string message; Error(int c, std::string msg) : code(c), message(std::move(msg)) {} std::string to_string() const { return "Int error: " + std::to_string(code) + " - " + message; } }; // 网络错误枚举的特化 template<> struct Error<NetworkErrorCode> { NetworkErrorCode code; std::string message; Error(NetworkErrorCode c, std::string msg) : code(c), message(std::move(msg)) {} std::string to_string() const { // 可以把枚举转成对应的字符串 switch(code) { case NetworkErrorCode::CONNECTION_FAILURE: return "Network error: CONNECTION_FAILURE - " + message; case NetworkErrorCode::EMPTY_RESPONSE: return "Network error: EMPTY_RESPONSE - " + message; default: return "Network error: UNKNOWN - " + message; } } };
使用示例
Error<int> int_err(500, "Internal server error"); Error<NetworkErrorCode> net_err(NetworkErrorCode::EMPTY_RESPONSE, "Server sent empty data"); std::cout << int_err.to_string() << std::endl; std::cout << net_err.to_string() << std::endl;
这种方案的优势是可以为每种错误类型定制专属逻辑,但缺点是新增错误类型时需要编写对应的特化结构体,相对繁琐一些。
额外小提示
- 如果需要把错误码统一转换成int用于日志或外部接口,可以给Error结构体添加一个
to_int()方法,用std::visit处理variant的不同类型,或者在模板特化中分别实现转int的逻辑。 - 如果你还需要支持错误链(比如嵌套错误),可以在Error结构体里添加一个
std::optional<Error>成员,用来存储原始错误。
内容的提问来源于stack exchange,提问作者p0fi
相关产品推荐
相关产品推荐

