C++ 为什么“std::enable_if”需要默认值?

C++ 为什么“std::enable_if”需要默认值?,c++,c++11,templates,sfinae,C++,C++11,Templates,Sfinae,如果使用std::enable\u,为什么必须在此std::enable\u中使用默认值(::type=0 我看到了一些例子,在没有它的情况下它是有效的。比如说 #包括 #包括 模板 无效数据(T){ std::cout我无法重现您的错误;无论如何,我在使用类调用do_stuff()时遇到了一个错误 do_stuff(std::string{"abc"}) 这是因为do_stuff()变成do_stuff(),并且模板值不能是std::string类型(并且不是默认值零) 建议:将函数重写为第

如果使用
std::enable\u,为什么必须在此
std::enable\u中使用默认值(
::type=0

我看到了一些例子,在没有它的情况下它是有效的。比如说

#包括
#包括
模板
无效数据(T){

std::cout我无法重现您的错误;无论如何,我在使用类调用
do_stuff()
时遇到了一个错误

do_stuff(std::string{"abc"})
这是因为
do_stuff()
变成
do_stuff()
,并且模板值不能是
std::string类型(并且不是默认值零)

建议:将函数重写为第二个位置的值的类型

template <typename T, // .....................................VVV  int, not T
          typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void do_stuff(T t) {
    std::cout << "do_stuff integral\n";
}

template <typename T, // ..................................VVV  int, not T
          typename std::enable_if<std::is_class<T>::value, int>::type = 0>
void do_stuff(T t) {
    std::cout << "do_stuff class\n";
}
模板
无效数据(T){

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

这就是为什么上面使用
std::enable_if
的技术需要默认参数的原因。这是一个伪参数,所以默认参数值无关紧要(
0
是一个自然选择)

你可以把你的例子简化为一个简单的例子

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

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

您的代码完全相同,只是第二个模板参数未命名(无需为伪参数指定名称)


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

注意,在第二个参数的声明中,关键字
typename
用于完全不同的角色

std::enable_if<std::is_class<T>::value, T>::type

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

在您引用的示例中,调用
enable_if
是默认参数。它看起来像这样:
typename=std::enable_if::type
。它是模板类型参数(名称省略)给定一个默认值。在您的代码中,您有一个类型为
std::enable_if::type
的非类型模板参数,无法推断它。@IgorTandetnik当我这样做时,我得到:temp.cpp:13:6:错误:重新定义“模板void do_stuf(T)”void do_stuf(T){^~~~~~~~temp.cpp:7:6:注意:'template void do_stuff(T)'之前在这里声明void do_stuff(T){文章中的示例也没有编译。可能是时候找一篇更好的文章了。@IgorTandetnik。这是Jonathan Müller的帖子()。应该可以:)@DanielLangr.是的,这是一个有效的例子,我在问为什么::type=0必须存在?尝试删除它。是的,最好的是:
template
。问题是,为什么这个nullptr必须存在?@MiCha这个非类型模板参数的值应该来自哪里?基本上你有
template void do_stuff(T)
-编译器需要
x
@MiCha的值-不需要使用
T
(直接或作为指针);
int=0
工作得很好。无论如何,必须使用
=nullptr
(或
=0
,在我的示例中)来避免显式显示它;否则您必须调用
do\u stuff(42)
而不是
dou_stuff(42);
。看来你是对的。我仍然缺少一些东西。我的理解是,如果编译器将第一个T推断为int,那么第二个T也是int,那么->x是int。你说的“x的参数”是什么意思,类型?Thx用于clarification@MiCha:您似乎忽略了以下事实:在这两种情况下,第一个模板参数都是类型参数,而第二个是非类型参数(“值”参数)在我的示例中,参数
T
代表
int
,但
x
并不代表
int
x
是一个值参数,它代表
int
类型的某个值。编译器应该使用什么值?编译器不知道。@AnT是的,你是对的,但这就是你的在我的例子中,第二个模板参数是type(关键字typename)如果enable_if::type是T的别名,那么如果T是int,那么::type就是int,那么为什么type参数还需要默认值呢?Thx@MiCha:不,不,不,不,不。您的示例与我的示例完全相同。您只是误解了第二个参数中
typename
一词的含义。不,这并不意味着这是一个类型参数你的第二个参数是一个值参数,就像我的一样。看看更新的答案。就是这样!谢谢!
template <typename T, // .....................................VVV  int, not T
          typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void do_stuff(T t) {
    std::cout << "do_stuff integral\n";
}

template <typename T, // ..................................VVV  int, not T
          typename std::enable_if<std::is_class<T>::value, int>::type = 0>
void do_stuff(T t) {
    std::cout << "do_stuff class\n";
}
template <typename T, T x> 
void foo(T t) {}

int main()
{
  foo(42);
}
error: no matching function for call to 'foo(int)'
note:   template argument deduction/substitution failed:
note:   couldn't deduce template parameter 'x'
std::enable_if<std::is_class<T>::value, T>::type
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);
}