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

问题描述 投票:0回答:5

我有以下使用

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

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

c++ templates c++14 sfinae enable-if
5个回答
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() { }

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

它们都是“type”的模板函数

template<class,class>void()
。事实上,第二个类型参数具有不同的 default 值并不重要。这就像期望两个不同的
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
,编译器无法确定这两者是否冲突 - 并且您会注意到它们实际上永远不会冲突。


12
投票

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

f
的情况下,我们有两个带有模板参数
<typename, X*>
的函数模板,其中类型
X
dependent 于第一个模板 argument 的类型。在
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() { }

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


0
投票

这个话题已经很晚了,但由于问题背后的想法是拥有更清晰的语法,我想提到另一种使用函数模板专业化的方法。它不像使用

enable_if
那样通用,但如果我们想要的只是针对不同类型的
f
的不同实例,那么效果很好。

template <typename T>
void f() = delete;

template <>
void f<int>() { }

template <>
void f<double>() { }

如果模板被实例化为除

int
double
之外的任何类型,您将收到以下错误消息

<source>:18:5: error: call to deleted function 'foo'
   18 |     foo<char>();
      |     ^~~~~~~~~
<source>:4:6: note: candidate function [with T = char] has been explicitly deleted
    4 | void foo() = delete;
      |      ^
1 error generated.

-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() { }
© www.soinside.com 2019 - 2024. All rights reserved.