为什么将字符串传递给调用std::format的模板无法编译?



下面的代码片段无法在最新版本的MSVC (Visual Studio 2022 17.2.2)上编译。同样的代码片段似乎在以前的编译器版本中工作得很好。

#include <iostream>
#include <format>
template <typename First, typename... Args>
inline auto format1(First&& first, Args&&... args) -> decltype(std::format(first, std::forward<Args>(args)...))
{
return std::format(std::forward<First>(first), std::forward<Args>(args)...);
}
int main()
{
std::cout << format1("hi {} {}", 123, 456);
}

编译器发出以下错误:

1>ConsoleApplication3.cpp(10,24): message: failure was caused by a .读取超出其生命周期的变量1>ConsoleApplication3.cpp(10,24): message:参见'first'的用法ConsoleApplication3.cpp(14): message:参见参考函数模板实例化'std::string format&ltchar(&)[9],int,int>(首先,int &&,int &&)'被编译为1>
用1>[1比;First=const char (&)[9] 1>)

似乎以某种方式将字符串字面值转发给std::format会使编译器认为它们在生命周期之外被使用。我尝试改变功能以接受const First& first和各种其他变体,但错误仍然存在。

据我所知,当First被演绎为const引用时,它的生命周期应该扩展到被调用函数的范围。

为什么会出现这个错误?我该如何解决这个问题?


进一步调查,这似乎是std::format使用的特定内容。

当提供字符串字面值时,此代码段工作正常:

template <std::size_t COUNT>
inline auto format2(const char (&first)[COUNT])
{
std::cout << first;
}

而这个没有:

template <std::size_t COUNT>
inline auto format2(const char (&first)[COUNT])
{
std::format(first);
}

P2216之后,std::format要求格式字符串必须是一个核心常量表达式。在您的示例中,编译失败是因为函数参数First不是常量表达式。

解决方法是使用std::vformat,它适用于运行时格式字符串
template<typename First, typename... Args>
auto format1(First&& first, Args&&... args) {
return std::vformat(
std::forward<First>(first),
std::make_format_args(args...));
}

演示如果你真的想使用std::format,你可以传入一个lambda,它返回一个字符串字面值

template<typename First, typename... Args>
auto format1(First first, Args&&... args) {
return std::format(first(), std::forward<Args>(args)...);
}
int main() {
std::cout << format1([]{ return "hi {} {}"; }, 123, 456);
}

演示

相关内容

最新更新