#include <concepts>
#include <format>
#include <string_view>
#include <type_traits>
template <typename ... Args>
struct FmtString {
std::format_string<Args ...> text;
template <typename StringCompatible>
requires (std::convertible_to<StringCompatible, std::string_view>)
consteval FmtString(StringCompatible const& description)
: text(description)
{}
};
template <typename ... Args>
void fails(FmtString<Args ...> const&, Args&& ...) noexcept {}
template <typename... Args>
using FmtStringArgIdentity = FmtString<std::type_identity_t<Args> ...>;
template <typename ... Args>
void works(FmtStringArgIdentity<Args ...> const&, Args&& ...) noexcept {}
int main() {
works("test {}", 42);
fails("test {}", 42);
}
GCC error message:
<source>: In function 'int main()':
<source>:28:10: error: no matching function for call to 'fails(const char [8], int)'
28 | fails("test {}", 42);
| ~~~~~^~~~~~~~~~~~~~~
<source>:18:6: note: candidate: 'template<class ... Args> void fails(const FmtString<Args ...>&, Args&& ...)'
18 | void fails(FmtString<Args ...> const&, Args&& ...) noexcept {}
| ^~~~~
<source>:18:6: note: template argument deduction/substitution failed:
<source>:28:10: note: mismatched types 'const FmtString<Args ...>' and 'const char [8]'
28 | fails("test {}", 42);
| ~~~~~^~~~~~~~~~~~~~~
Compiler returned: 1
Looking on the source code of libfmt I was able to make my code work. Unfortunately, I don't understand why this works or why the template argument deduction fails.
What is the problem with the fails
call and why does the works
call solve this?
Here is a simpler example with the same effect:
You cannot deduce
int
from12.0
that would be used to convert to afoo<int>
. There is simply no relation betweendouble
used as template argument to the constructor andint
used to instantiatefoo<int>
. Anyfoo<T>
has a converting constructor fromdouble
tofoo<T>
.std::type_identity
is a non-deduced context. Forworks<T>
, theT
is only deduced from42
, andworks<int>
is instantiated to getworks<int>(foo<int>,int)
. Only now the implicit conversion kicks in and12.0
can be converted tofoo<int>
via the converting constructor.