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

Jea*_*ier 29 c++ templates sfinae enable-if c++14

我有以下案例可以使用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() { }
Run Code Online (Sandbox Code Playgroud)

现在,我在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() { }
Run Code Online (Sandbox Code Playgroud)

但现在海湾合作委员会(5.2)抱怨:

error: redefinition of 'template<class T, class> void g()'
       void g() { }
Run Code Online (Sandbox Code Playgroud)

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

Yak*_*ont 37

我们删除一些代码.

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() { }
Run Code Online (Sandbox Code Playgroud)

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

它们都是"类型"的模板函数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() { }
Run Code Online (Sandbox Code Playgroud)

在这里我们无法删除该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() { }
Run Code Online (Sandbox Code Playgroud)

我还更换了使用的typenameclass.我怀疑你的困惑是因为typename有两个含义 - 一个作为一种template参数的标记,另一个作为依赖类型的消除歧义.

这里第二个参数是一个指针,其类型取决于第一个.编译器无法在没有先替换类型的情况下确定这两者是否存在冲突T- 并且您将注意到它们实际上永远不会发生冲突.


Okt*_*ist 10

enable_if_t<B>只是一个别名typename enable_if<B>::type.让我们替换它,g以便我们可以看到f和之间的真正区别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() { }
Run Code Online (Sandbox Code Playgroud)

在的情况下f,我们都与模板参数的两个函数模板<typename, X*>,其中类型X取决于第1模板的类型参数.如果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() { }
Run Code Online (Sandbox Code Playgroud)