There’re a couple of questions that mention the problem [this, this]. But I’m more interested in the reason why that doesn’t work.
Consider this example. The original intent should be self-explanatory.
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"}*/);
This will give a deduction/substitution error:
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"}*/);
|| | ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If I explicityly specify T
as it asks, I get another error, which makes no sense to me
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) {
|| | ^~~~~~~~~~~~
I am not sure if it doesn’t work or not supposed to work at all. But when the name lookup happens, I figure, there’s only one make_reflect
function template, which can only have mem_refl
as an argument.
I would understand if the compiler couldn’t infer mem_relf
from the brace-enclosed initializer list, but I’m not sure that the error implies it. The deduction guide kicks in if I write make_reflect(mem_refl{&foo::a, "a"})
, so I figure the problem lies somewhere in the order of deduction/substitution.