为什么在模板参数中使用 enable_if_t 会抱怨重定义?

44

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

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

8
你的第二段代码使用了默认模板参数。它们不是函数签名的一部分,因此你正在声明具有相同签名的两个函数模板 = 重定义。相应的enable_if_t的用法是字面意思为 std::enable_if_t<std::is_same<int, T>::value>* = nullptr - dyp
你可以在其中一个声明中添加一个虚拟模板参数,例如在 enable_if 后面加上 , typename = void - Piotr Skotnicki
2
@Jean-MichaëlCelerier,您不需要在std :: enable_if_t前面加上typename。 获得了更多的字符。 - bogdan
@Oktalist 刚刚尝试了一下,但我的编译器(gcc 4.9)抱怨 *=0 而不是 *=nullptr - Jean-Michaël Celerier
这就是为什么我没有说 *=0。我将 int 作为 enable_if_t 的第二个参数提供,而不是默认的 void,因此 0 是一个整数字面量,而不是空指针字面量。 - Oktalist
显示剩余6条评论
4个回答

58

让我们删除一些代码。

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

你会感到惊讶吗,如果编译器拒绝上述两个模板?它们都是"类型"template<class,class>void()的模板函数。第二个类型参数具有不同的默认值这一事实并不重要。这就像期望具有不同默认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有两个含义——一种是模板参数的标记,另一种是依赖类型的消歧标记。
这里第二个参数是一个指针,其类型依赖于第一个参数。编译器不能确定这两个是否冲突,除非先用类型T进行替换,你会注意到它们实际上永远不会冲突。

12

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*>,其中类型 X 取决于第一个模板参数的类型。而在函数 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() { }

如果我可以接受多个答案,我也会接受你的,因为它同样有效!谢谢! - Jean-Michaël Celerier
没问题,Yakk的解释稍微好一些。 - Oktalist

4

对于函数返回类型,你需要查找以下内容:

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


-7

你缺少了一个"::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() { }

3
std::enable_if_tstd::enable_if 的一个嵌套别名模板,其对应的类型定义在 ::type 中。 - Piotr Skotnicki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接