为什么 `std::enable_if` 需要默认值?

MiC*_*Cha 5 c++ templates sfinae c++11

为什么我必须::type = 0在这种std::enable_if用法中使用默认值( )?

\n\n

我看到一些例子,没有它也能工作。例如https://foonathan.net/blog/2015/11/30/overload-resolution-4.html

\n\n
#include<iostream>\n#include<type_traits>\n\ntemplate <typename T,\n          typename std::enable_if<std::is_integral<T>::value, T>::type = 0>\nvoid do_stuff(T t) {\n    std::cout << "do_stuff integral\\n";\n}\n\ntemplate <typename T,\n          typename std::enable_if<std::is_class<T>::value, T>::type = 0>\nvoid do_stuff(T t) {\n    std::cout << "do_stuff class\\n";\n}\n\nint main()\n{\n    do_stuff(32);\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我收到错误消息:

\n\n
temp.cpp:6:6: note:   template argument deduction/substitution failed:\ntemp.cpp:18:13: note:   couldn\'t deduce template parameter \xe2\x80\x98<anonymous>\xe2\x80\x99\n
Run Code Online (Sandbox Code Playgroud)\n\n

应推论为

\n\n
template <template T, int>\nvoid do_stuff(T t)\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是有效的代码。\n我做错了什么?(海湾合作委员会版本7.4.0

\n

AnT*_*AnT 5

编译器清楚地告诉您问题是什么:在模板声明中,您指定了一个无法推导的额外模板非类型参数。您期望编译器如何推断出该非类型参数的正确值?从何而来?

这正是上述使用技术std::enable_if需要默认参数的原因。这是一个虚拟参数,因此默认参数值并不重要(这0是一个自然的选择)。

您可以将示例简化为

template <typename T, T x> 
void foo(T t) {}

int main()
{
  foo(42);
}
Run Code Online (Sandbox Code Playgroud)

生产

error: no matching function for call to 'foo(int)'
note:   template argument deduction/substitution failed:
note:   couldn't deduce template parameter 'x'
Run Code Online (Sandbox Code Playgroud)

编译器可以推断出T( T == int) 是什么,但编译器无法推断出 的参数x

您的代码完全相同,只是您的第二个模板参数未命名(无需为虚拟参数命名)。


从您的评论来看,您似乎对代码中第二个参数的声明中存在关键字感到困惑typename,这使您相信第二个参数也是类型参数。后者是不正确的。

请注意,第二个参数的声明关键字typename用于完全不同的角色。这个关键字只是消除了语义的歧义

std::enable_if<std::is_class<T>::value, T>::type
Run Code Online (Sandbox Code Playgroud)

它告诉编译器嵌套名称实际上代表类型type的名称,而不是其他名称。(您可以在此处阅读有关此用法的信息:Why do we need typename here?以及Where and Why do I have to put the "template" and "typename" keywords?typename

这种用法typename不会将模板的第二个参数转换为类型参数。模板的第二个参数仍然是非类型参数。

这是另一个简化的示例,说明了代码中发生的情况

struct S { typedef int nested_type; };

template <typename T, typename T::nested_type x>
void bar(T t)
{}

int main()
{
  S s;
  bar<S, 42>(s);
}
Run Code Online (Sandbox Code Playgroud)

请注意,即使第二个参数的声明以 a 开头typename,它仍然声明一个非类型参数。

  • @MiCha:您似乎忽略了这样一个事实:在这两种情况下,第一个模板参数是 **类型** 参数,而第二个是 **非类型** 参数(“值”参数)。在我的示例中,参数“T”代表“int”,但“x”不代表“int”。`x` 是一个**值**参数,它代表 `int` 类型的**某个值**。编译器应该使用什么值?编译器不知道。 (2认同)