Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does enable_if_t in template arguments complains about redefinitions?

I have the following case that works using std::enable_if :

template<typename T,          typename std::enable_if<std::is_same<int, T>::value>::type* = nullptr> void f() { }  template<typename T,          typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr> void f() { } 

Now, I saw in cppreference the new syntax, much cleaner in my opinion : typename = std::enable_if_t<std::is_same<int, T>::value>>

I wanted to port my code :

template<typename T,          typename = std::enable_if_t<std::is_same<int, T>::value>> void g() { }  template<typename T,          typename = std::enable_if_t<std::is_same<double, T>::value>> void g() { } 

But now GCC (5.2) complains :

error: redefinition of 'template<class T, class> void g()'        void g() { } 

Why is that so ? What can I do to have the new, more concise syntax in this case if this is possible ?

like image 779
Jean-Michaël Celerier Avatar asked Jul 19 '15 10:07

Jean-Michaël Celerier


People also ask

What is a template argument?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

How many template arguments are there?

1) A template template parameter with an optional name. 2) A template template parameter with an optional name and a default. 3) A template template parameter pack with an optional name.

What is Typename C++?

" typename " is a keyword in the C++ programming language used when writing templates. It is used for specifying that a dependent name in a template definition or declaration is a type.


1 Answers

Let's remove some code.

template<   class T,   class U/* = std::enable_if_t<std::is_same<int, T>::value>*/  > void g() { }  template<   class T,   class U/* = std::enable_if_t<std::is_same<double, T>::value>*/  > void g() { } 

would you be surprised if the compiler rejected the two above templates?

They are both template functions of "type" template<class,class>void(). The fact that the 2nd type argument has a different default value matters not. That would be like expecting two different print(string, int) functions with different default int values to overload. ;)

In the first case we have:

template<   typename T,   typename std::enable_if<std::is_same<int, T>::value>::type* = nullptr > void f() { }  template<   typename T,   typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr > void f() { } 

here we cannot remove the enable_if clause. Updating to enable_if_t:

template<   class T,   std::enable_if_t<std::is_same<int, T>::value, int>* = nullptr > void f() { }  template<   class T,   std::enable_if_t<std::is_same<double, T>::value, int>* = nullptr > void f() { } 

I also replaced a use of typename with class. I suspect your confusion was because typename has two meanings -- one as a marker for a kind of template argument, and another as a disambiguator for a dependent type.

Here the 2nd argument is a pointer, whose type is dependent on the first. The compiler cannot determine if these two conflict without first substituting in the type T -- and you'll note that they will never actually conflict.

like image 97
Yakk - Adam Nevraumont Avatar answered Sep 21 '22 13:09

Yakk - Adam Nevraumont