基本信息
文件名称:C++中的std--format?如何实现编译期格式检查.docx
文件大小:16.43 KB
总页数:3 页
更新时间:2025-05-21
总字数:约2.14千字
文档摘要

C++中的std::format?如何实现编译期格式检查

C++20的std::format是一个很神奇、很实用的工具,最神奇的地方在于它能在编译期检查字符串的格式是否正确,而且不需要什么特殊的使用方法,只需要像使用普通函数那样传参即可。

#includeformat

inta=1;

std::strings1=std::format(a:{},a);//OK

std::strings2=std::format(a:{},b:{},a);//编译错误

C++20的std::format来自一个著名的开源库{fmt}。在C++20之前,fmt需要为每个字符串字面量创建不同的类型才能实现编译期格式检查。fmt提供了一个FMT_STRING宏以简化使用的流程。

#includefmt/format.h

inta=1;

std::strings1=fmt::format(FMT_STRING(a:{}),a);//OK

std::strings2=fmt::format(FMT_STRING(a:{},b:{}),a);//编译错误

C++20有了consteval后就不用这么别扭了。consteval函数与以前的constexpr函数不同,constexpr函数只有在必须编译期求值的语境下才会在编译期执行函数,而consteval函数在任何情况下都强制编译期求值。std::format就是利用consteval函数在编译期执行代码,来检查字符串参数的格式。

然而std::format自身不能是consteval函数,只好曲线救国,引入一个辅助类型std::format_string,让字符串实参隐式转换为std::format_string。只要这个转换函数是consteval函数,并且把格式检查的逻辑写在这个转换函数里面,照样能实现编译期的格式检查。

这里我们实现了一个极简版的format,可以检查字符串中{}的数量是否与参数的个数相同。format_string的构造函数就是我们需要的隐式转换函数,它是一个consteval函数。若字符串中{}的数量不对,则代码会执行到throw这一行。C++的throw语句不能在编译期求值,因此会引发编译错误,从而实现了在编译期检查出字符串的格式错误。

namespacemy{

templateclass...Args

classformat_string{

private:

std::string_viewstr;

public:

templateclassT

requiresstd::convertible_toconstT,std::string_view

constevalformat_string(constTs)

:str(s)

std::size_tactual_num=0;

for(std::size_ti=0;i+1str.length();i++){

if(str[i]=={str[i+1]==}){

actual_num++;

constexprstd::size_texpected_num=sizeof...(Args);

if(actual_num!=expected_num){

throwstd::format_error(incorrectformatstring);

std::string_viewget()const{returnstr;}

templateclass...Args

std::stringformat(format_stringstd::type_identity_tArgs...fmt,Args...args){

//省略具体的格式化逻辑

}

有一个细节,此处format函数的参数写的是format_stringstd::type_identity_tArgs...,直接写format_stringArgs...是无法隐式转换的,因为模板实参推导(templateargumentdeduction)不会考虑隐式转换,C++20提供了一个工具std::type_identity可以解决这个问题。std::type_identity其实就是一个关于类型的恒等函数,但是这么倒腾一下就能在模板实参推导中建立非推导