Why does enable_if_t in template arguments complains about redefinitions?

生来就可爱ヽ(ⅴ<●) 提交于 2019-11-27 21:06:30

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>();
}

http://ideone.com/TB36gH

see also

http://ideone.com/EfLkQy

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() { }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!