C++: how to test if an instance of a class contains a given member in a template function?
I googled this, looked over examples that are supposed to work... But still don't understand why?!...
The first thing I could have thought of is using traits and template specialization, seems this doesn't work.
Indeed, this is OK:
>> cat main.cpp
#include <iostream>
#include <type_traits>
struct point2D {
double x = 0.;
double y = 0.;
};
struct point3D {
double x = 0.;
double y = 0.;
double z = 0.;
};
template <typename T> struct has_z : std::false_type {}; // default case.
template <> struct has_z<point3D> : std::true_type {}; // specialized case.
template<typename T> void print(T const & obj) {
std::cout << obj.x << " " << obj.y;
//if (has_z<T>::value) std::cout << obj.z;
std::cout << std::endl;
};
int main() {
point2D pt2D;
print(pt2D);
point3D pt3D;
print(pt3D);
}
>> g++ -o main main.cpp
>> ./main
0 0
0 0
But, uncommenting the commented line gives:
>> g++ -o main main.cpp
main.cpp: In instantiation of ‘void print(const T&) [with T = point2D]’:
main.cpp:27:8: required from here
main.cpp:21:41: error: ‘const struct point2D’ has no member named ‘z’
21 | if (has_z<T>::value) std::cout << obj.z;
| ~~~~^
What is the minimal change to get this to work (with C++20) and why?
You need to use:
template<typename T> void print(T const & obj) {
std::cout << obj.x << " " << obj.y;
if constexpr(has_z<T>::value) std::cout << obj.z;
std::cout << std::endl;
};
That way, the compiler knows the expression is a compile-time constexpr and can remove the .z
read. Otherwise, the if
is runtime and the compiler can't generate one possibility (the read of .z
, never mind it would never happen to run)