6

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

1 Answer 1

10

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(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

Sign up to request clarification or add additional context in comments.

2 Comments

thanks, can you please explain why First is not a constant expression? can I somehow declare my template to correctly deduce a constant expression?
Note that arguments to make_format_args shouldn't be forwarded, see e.g. eel.is/c++draft/format#functions-2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.