2012-03-09 64 views
12

我很惊讶,下面的代码产生了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)修复该问题。为什么编译器不能从默认参数中推导出类型?

A template type-parameter cannot be deduced from the type of a function default argument.

在C++ 11:

回答

15

在C++ 03,说明书中明确被用来推断模板参数(C++ 03§14.8.2/ 17)禁止所述默认参数,可以为函数模板提供默认模板参数:

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

不过,需要默认模板参数。如果未提供默认模板参数,则默认函数参数仍不适用于模板参数推演。具体而言,适用于下列(C++ 11 14.8.2.5/5):

The non-deduced contexts are:

...

  • A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
+10

虽然说“因为标准如此”是一个有效的答案,所以很高兴知道其背后的原因。 – 2012-03-09 04:39:16

+1

除了其他原因,函数的不同声明可以声明不同的默认参数(我相当肯定这同样适用于函数模板。) – 2012-03-09 04:42:25

+1

@James:不,不允许声明不同的默认参数。甚至不允许多个声明将相同的默认值赋予相同的参数。 8.3.6说:“一个默认的参数不能被后面的声明重新定义(甚至不会被重新定义)。”当然,这只适用于非模板功能。对于模板函数,它看起来像默认参数只能在初始声明中提供。 – 2013-09-03 05:03:33

4

一个很好的理由可能是

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); 

即使当为一个写的,它仍然像在一个两个功能,均未在任何意义上的“优选的” 。你正在调用单参数函数;双参数实际上是一个不同的功能。默认参数表示法将它们合并为一个。

如果重载是有你所要求的行为,那么为了保持一致性,它必须在模板分成两个定义的情况下工作。这是没有道理的,因为那么扣除将会从不被调用的不相关函数中抽取类型!如果没有实施,这意味着与“默认论证”相比,重载不同的参数列表长度成为“二等公民”。

如果重载和违约之间的差异完全隐藏给客户端,那就好了。

7

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

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. 执行重载分辨率; (B)被选择
  3. 形式呼叫,实例化所述默认参数

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

+0

声音对我来说合理。 – 2017-01-11 10:22:21

相关问题