user12956443
user12956443

Reputation:

C++ template argument without a name

I have come across the following code snippet:

template <typename T, typename = void>
struct test {
  int t = sizeof(T);
};

I know that in typename = void, void is a default argument but it doesn't have a name! What is it useful for and what does it even mean?

Upvotes: 9

Views: 1080

Answers (2)

warrior_monk
warrior_monk

Reputation: 433

I personally like to see it as a case of default template (type)argument to sfinae out overloads which does not meet certain criteria. First thing first, giving name to a default template argument is just fine, so the following is correct:

template <typename T, typename sometype= void>
struct test {
  int t = sizeof(T);
};

In the above case , clearly the type argument sometype is not used anywhere in the struct test. But what if instead of setting default value to void, we set it using some compile time conditions so that the template function is only valid for integral types like so ?

(borrowing code's from nathan's answer)

 template <typename T>
struct test<T, typename sometype = std::enable_if_t<std::is_integral_v<T>>> {          
  int t = 42;
};

If the T has type integral then sometype is defined otherwise the given template is ignored making use of sfinae.

Additionally, you can drop "sometype" to write :

template <typename T>
struct test<T, typename = std::enable_if_t<std::is_integral_v<T>>> {          
  int t = 42;
};

Finally compare this with the default values used in function declarations:

void foo(int = 9);  //Function declaration can have default values without names too.
void foo (int a ) 
{
//some code
}

Upvotes: 2

NathanOliver
NathanOliver

Reputation: 180500

This is used for specializations in conjunction with SFINAE. Doing this allows you to have code like

template <typename T, typename = void>
struct test {
  int t = sizeof(T);
};

template <typename T>
struct test<T, std::enable_if_t<std::is_integral_v<T>>> {
//             ^^  this part "fills in" the void    ^^
  int t = 42;
};

template <typename T>
struct test<T, std::enable_if_t<std::is_floating_point_v<T>>> {
//             ^^     this part "fills in" the void       ^^
  int t = 21;
};

int main()
{
    test<int> i;
    std::cout << i.t << "\n";
    test<double> d;
    std::cout << d.t;
}

which outputs

42
21

Without the typename = void, we would not be able to add these specializations because there would be no second parameter the enable_if_t part could "fill in".

Upvotes: 14

Related Questions