如何保留函数参数consteval属性,实现支持fmt编译时格式检查的普通调用语法
解决方案
你可以直接使用fmt库提供的fmt::format_string类型作为构造函数的第一个入参,就能保留编译期格式检查,同时恢复你想要的普通调用语法,完整实现如下:
#include <fmt/format.h> #include <stdexcept> struct my_exception : std::runtime_error { template<typename... Args> my_exception(fmt::format_string<Args...> fmt_str, Args&&... args) : std::runtime_error{fmt::format(std::move(fmt_str), std::forward<Args>(args)...)} {} };
实现后你就可以直接使用目标语法调用:
throw my_exception("error: {}", 123);
如果格式字符串和参数类型、数量不匹配,编译期就会直接报错,完全满足你的要求。
原理解释
你最初的朴素实现失败的核心原因是:通用的模板参数推导不会强制格式字符串为编译期常量,丢失了fmt::format要求的consteval属性。
而fmt::format_string是fmt 8.0及以上版本专门为这类场景设计的类型,它的构造函数是consteval的,仅能接受编译期可见的字符串字面量,会在实例化阶段自动完成格式字符串和参数的匹配校验,完全保留编译期检查能力的同时不需要修改调用语法。
如果你的编译环境不支持C20,也可以用兼容C17的写法,调用时用FMT_STRING宏包裹格式字符串即可,同样能保留编译期检查:
// 兼容C++17的实现 struct my_exception : std::runtime_error { template<typename... Args> my_exception(Args&&... args) : std::runtime_error{fmt::format(std::forward<Args>(args)...)} {} }; // 调用方式 throw my_exception(FMT_STRING("error: {}"), 123);
内容的提问来源于stack exchange,提问作者Jean-Michaël Celerier
相关产品推荐
相关产品推荐

