4

When I first learned how to check a specific signature in a class, I was taught to use std::void_t and write some code like this:

template<typename T, typename =void>
class HAS:public false_type{};

template<typename T>
class HAS<T,void_t<decltype(declval<T>().print())>>:public true_type{};

And this snippet of code will check if a class has the method named "print()". It works well.

But when I tried to remove the std::void_t, it still worked.

The code looks like this:

template<typename T, typename = void>
class HAS:public false_type{};

template<typename T>
class HAS<T,decltype(declval<T>().print())>:public true_type{};

So I am confused if "std::void_t" is necessary to check if a class has a method with a specific signature? Or that's only a coincidence?

alfC
  • 14,261
  • 4
  • 67
  • 118
Kidsunbo
  • 1,024
  • 1
  • 11
  • 21

3 Answers3

10

This question explains in detail how void_t (otherwise known as the detection idiom) works. The key point is that the specialisation will only be considered if the type of the second template parameter evaluates to void.

In this case, it just so happens that your print() method returns void, so decltype(declval<T>().print()) is also void. But if your print() returned something else, say bool, then the specialisation would not match and would not be used.

Tristan Brindle
  • 16,281
  • 4
  • 39
  • 82
4

"void_t" is necessary to check if a class has a method with a specific signature?

+1 for the Tristan Brinde answer; but my answer is: std::void_t helps but (in your case) isn't necessary.

You can use the comma trick

decltype(std::declval<T>().print(), void())

The following is a compiling (in C++11 too; std::void_t is available only from C++17) full example

struct foo
 { };

struct bar
 { int print () const { return 0; } };

template <typename T, typename = void>
class HAS
   : public std::false_type
 { };

template <typename T>
class HAS<T, decltype(std::declval<T>().print(), void())>
   : public std::true_type
 { };

int main ()
 {
   static_assert( HAS<foo>::value == false, "!" );
   static_assert( HAS<bar>::value == true,  "!!" );
 }
max66
  • 65,235
  • 10
  • 71
  • 111
0

So I am confused if "std::void_t" is necessary

To me it is not entirely clear what you mean by that. If you mean the expression SFINAE which is utilized by void_t then the answer is yes. If you mean whether you need to explicitly mention void_t, then the answer is no.

For example in C++17 you can make use of the is_detected method, which is also part of std::experimental. For other approaches see the other answers.

#include <experimental/type_traits>

template < typename T >
using print_t = decltype(std::declval<T>().print());

template < typename T >
using has_print = std::experimental::is_detected< print_t, T >;

struct A {};

struct B { void print() {} };

int main()
{
  static_assert( has_print<A>::value == false );
  static_assert( has_print<B>::value == true  );
}

Live example

Henri Menke
  • 10,705
  • 1
  • 24
  • 42