为什么编译器不能从默认参数中推导出模板types?

我很惊讶下面的代码导致could not deduce template argument for T错误的could not deduce template argument for T

 struct foo { template <typename T> void bar(int a, T b = 0.0f) { } }; int main() { foo a; a.bar(5); return 0; } 

调用a.bar<float>(5)修复了这个问题。 为什么编译器不能从默认参数中推导出types?

在C ++ 03中,规范明确禁止使用默认参数来推导模板参数(C ++ 03§14.8.2/ 17):

模板types参数不能从函数默认参数的types推导出来。

在C ++ 11中,可以为函数模板提供默认的模板参数:

 template <typename T = float> void bar(int a, T b = 0.0f) { } 

但是,默认的模板参数是必需的。 如果没有提供默认的模板参数,默认的函数参数仍然不能用于模板参数的推导。 具体而言,以下适用(C ++ 11 14.8.2.5/5):

未推断的上下文是:

  • 参数types中使用的参数types中的模板参数,该参数具有正在进行参数推演的调用中使用的缺省参数。

总的来说,实现这一点会有一些技术上的困难。 请记住,模板中的默认参数在需要之前不会被实例化。 考虑一下:

 template<typename T, typename U> void f(U p = T::g()); // (A) template<typename T> T f(long, int = T()); // (B) int r = f<int>(1); 

今天通过执行以下步骤来解决这个问题:

  1. 试图推断候选人(A)和(B)的模板参数; (A),因此被淘汰。
  2. 执行重载parsing; (B)被选中
  3. 形成呼叫,实例化默认参数

为了从默认参数中推导出来,该默认参数必须在完成推演过程之前本身被实例化。 这可能会失败,导致SFINAE环境以外的错误。 也就是说,可能完全不适合打电话的候选人可能会引发错误。

一个很好的理由可能是这样的

 void foo(bar, xyzzy = 0); 

类似于一对重载。

 void foo(bar b) { foo(b, 0); } foo(bar, xyzzy); 

而且,有时将其重构成这样是有利的:

 void foo(bar b) { /* something other than foo(b, 0); */ } foo(bar, xyzzy); 

即使写成一个,它仍然是一个两个function,在任何意义上都不是“首选”。 你正在调用一个参数函数; 这两个论点实际上是一个不同的function。 默认参数表示法将它们合并为一个。

如果超载有你所要求的行为,那么为了保持一致性,当模板被分成两个定义的情况下,它必须工作。 这是没有意义的,因为那么扣除将从一个不被称为不相关的函数拉动types! 如果没有实现,则意味着与“默认论证”相比,重载不同的参数列表长度成为“二等公民”。

过载和违约之间的区别对客户是完全隐藏的。

    Interesting Posts