简体   繁体   中英

Why passing a string literal to a template calling std::format fails to compile?

The following code snippet fails to compile on the latest version of MSVC (Visual Studio 2022 17.2.2). The same snippet seemed to work just fine on previous compiler versions.

#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);
}

The compiler emits the following error:

1>ConsoleApplication3.cpp(10,24): message : failure was caused by a read of a variable outside its lifetime 1>ConsoleApplication3.cpp(10,24): message : see usage of 'first' 1>ConsoleApplication3.cpp(14): message : see reference to function template instantiation 'std::string format<const char(&)[9],int,int>(First,int &&,int &&)' being compiled 1>
with 1> [ 1> First=const char (&)[9] 1> ]

It seems that somehow forwarding a string literal to std::format makes the compiler think that they are used outside of their lifetime. I tried changing the function to accept const First& first and all sorts of other variants but the error remains.

As far as I understand, when First is deduced to a const reference, its lifetime should be extended to the scope of the invoked function.

So why do I get this error? How can I fix this?


Further investigating this, it seems like something specific to the use of std::format.

This snippet works fine when provided with a string literal:

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

Wheras this one doesn't:

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

After P2216 , std::format requires that the format string must be a core constant expression. In your case, the compilation fails because the function argument First is not a constant expression.

The workaround is to use std::vformat , which works for runtime format strings

template<typename First, typename... Args>
auto format1(First&& first, Args&&... args) {
  return std::vformat(
    std::forward<First>(first),
    std::make_format_args(std::forward<Args>(args)...));
}

Demo

If you really want to use std::format , you can pass in a lambda that returns a string literal

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);
}

Demo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM