Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is `void_t` necessary to check if a class has a method with a specific signature?

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?

like image 272
Kidsunbo Avatar asked Dec 07 '22 17:12

Kidsunbo


2 Answers

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.

like image 105
Tristan Brindle Avatar answered Dec 10 '22 05:12

Tristan Brindle


"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,  "!!" );
 }
like image 41
max66 Avatar answered Dec 10 '22 06:12

max66