#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
intfrom12.0that would be used to convert to afoo<int>. There is simply no relation betweendoubleused as template argument to the constructor andintused to instantiatefoo<int>. Anyfoo<T>has a converting constructor fromdoubletofoo<T>.std::type_identityis a non-deduced context. Forworks<T>, theTis only deduced from42, andworks<int>is instantiated to getworks<int>(foo<int>,int). Only now the implicit conversion kicks in and12.0can be converted tofoo<int>via the converting constructor.