Recently I discovered that the following code compiles on a few major compilers, and then throws at runtime:
std::cout << std::format("{:*<{}}", 10, "Hello") << std::endl;
terminate called after throwing an instance of 'std::format_error'
what(): format error: argument used for width or precision must be a non-negative integer
It throws because "10" should come after "Hello", not before.
But the obvious question is: Why isn't it failing at compile-time? My understanding was that these arguments would be type-checked at compile-time, and obviously a const char*
can't be used as a width specifier. Why is this not a compile error?
If you don't understand why this is confusing, please know that the first argument of std::format()
is of type std::format_string<...>
. This type takes a string literal/string view at compile time (due to its consteval
constructor), and at compile-time it reads the contents of the given string to see if the format arguments match the format string. Therefore, a call of std::format("{}");
is guaranteed not to compile, since the string "{}" is read at compile-time as a format specifier, but the type lists show that no arguments were passed, so what would be put in that space?
std::stoi(10)
and that doesn't compile – Fibrillastd::format("{}");
, that actually wouldn't compile, and is not valid C++ code, because the first argument ofstd::format
is astd::format_string
which must be constructed at compile time, and during its construction, it considers whether the types passed tostd::format
are valid to be used for the formatting call. My point is that normally, these issues actually would be caught at compile-time, but not in this case? – Deipnosophist