为何C++模板实参推导无法处理大括号初始化列表?
C++模板参数推导问题:大括号初始化与函数模板的冲突
问题背景
已有相关讨论涉及类似现象,但本文重点探究其背后的原理。以下代码旨在通过模板实现结构体成员的反射功能:
示例代码
template <typename T> constexpr auto mem_vars = [] { static_assert(!sizeof(T), "mem_vars is not specialized"); }; template <typename T, typename M> struct mem_refl { M T::*mem_ptr; std::string_view name; }; template <typename T, typename M> mem_refl(M T::*, std::string_view) -> mem_refl<T, M>; template <typename T, typename... M> constexpr std::tuple<mem_refl<T, M>...> make_reflect(mem_refl<T, M>... m) { // todo: static asssert that all the fields have been specified return {m...}; } struct foo { std::string a; int b; }; template <> constexpr auto mem_vars<foo> = make_reflect({&foo::a, "a"} /*, mem_refl{&foo::b, "b"}*/);
编译错误详情
未显式指定模板参数T时的错误
编译时触发推导/替换错误:
serialization.cpp|51 col 44| error: no matching function for call to ‘make_reflect(<brace-enclosed initializer list>)’ || 51 | constexpr auto mem_vars<foo> = make_reflect({&foo::a, "a"} /*, mem_refl{&foo::b, "b"}*/); || | ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ serialization.cpp|40 col 41| note: candidate: ‘template<class T, class ... M> constexpr std::tuple<mem_refl<T, M>...> make_reflect(mem_refl<T, M>...)’ || 40 | constexpr std::tuple<mem_refl<T, M>...> make_reflect(mem_refl<T, M>... m) { || | ^~~~~~~~~~~~ serialization.cpp|40 col 41| note: template argument deduction/substitution failed: serialization.cpp|51 col 44| note: couldn’t deduce template parameter ‘T’ || 51 | constexpr auto mem_vars<foo> = make_reflect({&foo::a, "a"} /*, mem_refl{&foo::b, "b"}*/); || | ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
显式指定T为foo时的错误
按提示显式指定模板参数后,出现另一个困惑性错误:
serialization.cpp|51 col 49| error: too many arguments to function ‘constexpr std::tuple<mem_refl<T, M>...> make_reflect(mem_refl<T, M>...) [with T = foo; M = {}]’ || 51 | constexpr auto mem_vars<foo> = make_reflect<foo>({&foo::a, "a"} /*, mem_refl{&foo::b, "b"}*/); || | ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ serialization.cpp|40 col 41| note: declared here || 40 | constexpr std::tuple<mem_refl<T, M>...> make_reflect(mem_refl<T, M>... m) { || | ^~~~~~~~~~~~
核心疑问
当前作用域中仅存在一个make_reflect函数模板,仅接受mem_refl类型参数。我能理解编译器无法直接从大括号初始化列表推导mem_refl类型的情况,但如果显式写出make_reflect(mem_refl{&foo::a, "a"}),类模板推导指南就能正常生效。因此推测问题根源在于模板参数推导与类模板推导的执行顺序,但不确定这是语言特性限制还是用法错误。
内容的提问来源于stack exchange,提问作者Sergey Kolesnik
相关产品推荐
相关产品推荐

