如何优化C++20中使用格式化库抛出异常的写法?
更简洁的C++20格式化异常抛出方案
你可以通过封装模板函数、自定义异常类或者宏来简化带格式化信息的异常抛出,不用每次手动写std::format,以下是几种实用方案:
1. 封装模板函数(推荐)
写一个通用的模板函数,把格式化和抛异常的逻辑打包,同时利用C++20的std::format_string做编译期类型检查,避免格式字符串和参数不匹配的问题:
#include <stdexcept> #include <format> #include <utility> // 用于std::forward template<typename... Args> [[noreturn]] void throw_runtime_error(std::format_string<Args...> fmt, Args&&... args) { throw std::runtime_error( std::format(fmt, std::forward<Args>(args)...) ); }
使用时直接调用函数,写法简洁很多:
throw_runtime_error("Critical error! Code {}: {}", errno, strerror(errno));
如果需要抛出其他类型的异常(比如std::logic_error),可以扩展模板支持指定异常类型:
template<typename ExceptionType, typename... Args> [[noreturn]] void throw_formatted_exception(std::format_string<Args...> fmt, Args&&... args) { static_assert(std::is_base_of_v<std::exception, ExceptionType>, "ExceptionType must inherit from std::exception"); throw ExceptionType(std::format(fmt, std::forward<Args>(args)...)); } // 使用示例:抛出logic_error throw_formatted_exception<std::logic_error>("Invalid argument: {}", arg_value);
2. 自定义格式化异常类
如果需要区分不同的异常类型或扩展异常功能,可以写一个继承自标准异常的类,在构造函数里处理格式化:
#include <stdexcept> #include <format> #include <string> class formatted_runtime_error : public std::runtime_error { public: template<typename... Args> explicit formatted_runtime_error(std::format_string<Args...> fmt, Args&&... args) : std::runtime_error(std::format(fmt, std::forward<Args>(args)...)) {} };
使用时直接抛自定义异常:
throw formatted_runtime_error("Critical error! Code {}: {}", errno, strerror(errno));
这种方式后续还能给异常类添加额外成员(比如错误码)或方法,扩展性更强。
3. 宏定义(极致简洁但需谨慎)
如果追求最短的写法,可以用宏封装,但要注意宏的局限性(比如参数中的逗号需要用括号包裹、调试时栈信息不够清晰):
#include <stdexcept> #include <format> #define THROW_RUNTIME_ERROR(fmt, ...) \ throw std::runtime_error(std::format(fmt, __VA_ARGS__))
使用时:
THROW_RUNTIME_ERROR("Critical error! Code {}: {}", errno, strerror(errno));
若参数包含逗号(比如std::pair<int, int>),需用括号包裹参数避免解析错误:
THROW_RUNTIME_ERROR("Pair value: {}", (std::make_pair(1, 2)));
内容的提问来源于stack exchange,提问作者Nico Engels
相关产品推荐
相关产品推荐

