Search code examples
c++metaprogramming

C++: how to test if an instance of a class contains a given member in a template function?


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?


Solution

  • 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)