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 ?
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.
enable_if_t<B>
is just an alias for typename enable_if<B>::type
. Let's substitute that in g
so we can see the real difference between f
and g
:
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() { }
template<typename T,
typename = typename std::enable_if<std::is_same<int, T>::value>::type>
void g() { }
template<typename T,
typename = typename std::enable_if<std::is_same<double, T>::value>::type>
void g() { }
In the case of f
, we have two function templates both with template parameters <typename, X*>
, where the type X
is dependent on the type of the first template argument. In the case of g
we have two function templates with template parameters <typename, typename>
and it is only the default template argument which is dependent, so C++ considers that they are both declaring the same entity.
Either style can be used with the enable_if_t
alias:
template<typename T,
std::enable_if_t<std::is_same<int, T>::value>* = nullptr>
void f() { }
template<typename T,
std::enable_if_t<std::is_same<double, T>::value>* = nullptr>
void f() { }
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() { }
For a function return type, you're looking for the following:
template<typename T> std::enable_if_t< conditional, instantiation result > foo();
Example:
#include <iostream>
// when T is "int", replace with 'void foo()'
template<typename T>
std::enable_if_t<std::is_same<int, T>::value, void> foo() {
std::cout << "foo int\n";
}
template<typename T>
std::enable_if_t<std::is_same<float, T>::value, void> foo() {
std::cout << "foo float\n";
}
int main() {
foo<int>();
foo<float>();
}
see also
You're missing a "::type" ..
template<typename T,
typename = std::enable_if_t<std::is_same<int, T>::value>::type>
void g() { }
template<typename T,
typename = std::enable_if_t<std::is_same<double, T>::value>::type>
void g() { }
来源:https://stackoverflow.com/questions/31500426/why-does-enable-if-t-in-template-arguments-complains-about-redefinitions