为何需用std::type_identity_t解决此模板推导与隐式转换问题?
模板参数推导失败分析与std::type_identity_t的作用
代码示例
#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); }
编译错误信息
<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
问题原因与解决方案解析
1. fails函数推导失败的核心原因
模板参数推导阶段,编译器不会考虑隐式转换,只会直接匹配实参的原始类型和形参的模板类型。
fails函数的第一个形参是FmtString<Args...> const&,但传入的实参是字符串字面量const char[8],两者类型完全不匹配。此时编译器需要推导Args...,但:
- 从第一个参数推导:无法从字符串字面量直接得到
Args...,因为FmtString<Args...>的类型依赖于Args...,而std::format_string<Args...>需要结合占位符和后续参数才能确定类型,推导陷入死循环。 - 从第二个参数
42(int)推导:得到Args...为int,但此时第一个形参类型应为FmtString<int> const&,编译器不会自动尝试将const char[8]隐式转换为FmtString<int>(推导阶段不处理转换),因此推导失败。
2. std::type_identity_t的解决原理
std::type_identity_t<T>是C++20引入的类型别名,本质就是T,但它的关键作用是阻断模板参数推导:当模板参数出现在std::type_identity_t内部时,编译器不会从这个位置推导该模板参数。
在works函数中,第一个形参是FmtStringArgIdentity<Args...>,即FmtString<std::type_identity_t<Args>...>:
- 编译器会优先从第二个参数
Args&&...推导Args...,这里从42推导出Args为int。 - 随后将推导结果代入第一个形参,得到
FmtString<int> const&。 - 此时进入隐式转换检查阶段:字符串字面量
"test {}"可以通过FmtString的consteval构造函数转换为FmtString<int>(因为std::format_string<int>匹配该格式串的占位符),转换合法,函数调用成功。
内容的提问来源于stack exchange,提问作者Benjamin Buch
相关产品推荐
相关产品推荐

