I'm playing around with templates, compile-time evaluation and pointers to members.
I don't understand why the following code doesn't set a.x
to 42 and a.y
to 3.14. According to the output, FieldType
is unsigned
(printed as "j") in the A::x
case, and double
(printed as "d") in the A::y
case. Still, the if constexpr
chain doesn't match any of those cases.
#include <iostream>
#include <typeinfo>
#include <type_traits>
struct A {
unsigned x = 0;
double y = 0;
};
template<auto Field, typename Class>
void bar(Class* p) {
using FieldType = decltype(p->*Field);
if constexpr (std::is_same_v<FieldType, unsigned>) {
p->*Field = 42;
}
else if constexpr (std::is_same_v<FieldType, double>) {
p->*Field = 3.14;
}
else {
std::cout << "no matching case for field type '" << typeid(FieldType).name() << "'" << std::endl;
}
}
int main()
{
A a;
bar<&A::x>(&a);
bar<&A::y>(&a);
std::cout << std::endl;
std::cout << "a.x: " << a.x << std::endl;
std::cout << "a.y: " << a.y << std::endl;
return 0;
}
Playground on Compiler Explorer
Output:
no matching case for field type 'j'
no matching case for field type 'd'
a.x: 0
a.y: 0
Often a simple way to debug type issues in templates is to add static_assert
that fails on purpose. Adding static_assert(std::is_same_v<FieldType, unsigned>);
gives the following error message:
<source>: In instantiation of 'void bar(Class*) [with auto Field = &A::x; Class = A]':
<source>:30:15: required from here
<source>:14:24: error: static assertion failed
14 | static_assert(std::is_same_v<FieldType, unsigned>);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:14:24: note: 'std::is_same_v<unsigned int&, unsigned int>' evaluates to false
So the issue is that FieldType
is a reference and that's a different type than plain unsigned
.
std::is_same_v<std::remove_reference_t<FieldType>, unsigned>)
or std::is_same_v<FieldType, unsigned&>
both fix the problem. See it online.