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

时间:2015-07-19 10:57:33

标签: c++ templates c++14 sfinae enable-if

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

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

4 个答案:

答案 0 :(得分:35)

让我们删除一些代码。

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() { }
如果编译器拒绝了上述两个模板,你会感到惊讶吗?

它们都是&#34; type&#34;的模板函数。 template<class,class>void()。第二种类型参数具有不同的默认值这一事实并不重要。这就像期望两个不同的print(string, int)函数具有不同的默认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类型的情况下确定这两者是否冲突 - 并且您将注意到它们实际上永远不会发生冲突。

答案 1 :(得分:9)

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

答案 2 :(得分:2)

对于函数返回类型,您正在寻找以下内容:

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

答案 3 :(得分:-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() { }