为什么模板参数中的 enable_if_t 会抱怨重新定义?

Why does enable_if_t in template arguments complains about redefinitions?

我有以下使用 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() { }

现在,我在 cppreference 中看到了新的语法,在我看来更加清晰:typename = std::enable_if_t<std::is_same<int, T>::value>>

我想移植我的代码:

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

但是现在 GCC (5.2) 抱怨:

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

为什么会这样?如果可能的话,在这种情况下我该怎么做才能拥有新的、更简洁的语法?

您缺少“::类型”..

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

对于函数 return 类型,您正在查找以下内容:

template<typename T> std::enable_if_t< conditional, instantiation result > foo();

示例:

#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

另见

http://ideone.com/EfLkQy

enable_if_t<B> 只是 typename enable_if<B>::type 的别名。让我们将其替换为 g 以便我们可以看到 fg 之间的真正区别:

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

f 的情况下,我们有两个函数模板都带有模板参数 <typename, X*>,其中类型 Xdependent关于第一个模板 参数 的类型。在 g 的情况下,我们有两个带有模板参数 <typename, typename> 的函数模板,并且只有默认模板参数是依赖的,因此 C++ 认为它们都声明了相同的实体。

任何一种样式都可以与 enable_if_t 别名一起使用:

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

让我们删除一些代码。

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

如果编译器拒绝了上述两个模板,您会感到惊讶吗?

它们都是"type"template<class,class>void()的模板函数。第二类型参数具有不同的 default 值这一事实并不重要。这就像期望两个具有不同默认 int 值的不同 print(string, int) 函数重载一样。 ;)

在第一种情况下我们有:

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

这里我们不能删除enable_if子句。更新到 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() { }

我还将 typename 的用法替换为 class。我怀疑你的困惑是因为 typename 有两个含义——一个作为一种 template 参数的标记,另一个作为依赖类型的消歧器。

这里的第二个参数是一个指针,其类型依赖于第一个。如果不首先替换类型 T,编译器无法确定这两者是否冲突——您会注意到它们实际上永远不会冲突。