为什么编译器不能从默认参数中推断出模板类型?

时间:2012-03-09 04:32:29

标签: c++ templates default-arguments

我很惊讶以下代码导致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)可解决问题。为什么编译器不能从默认参数中推断出类型?

3 个答案:

答案 0 :(得分:18)

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

  

无法从函数默认参数的类型推导出模板类型参数

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

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

但是,默认模板参数是必需的。如果未提供缺省模板参数,则缺省函数参数仍不可用于模板参数推导。具体来说,以下适用(C ++ 11 14.8.2.5/5):

  

未推断的上下文是:

     

...

     
      
  • 函数参数的参数类型中使用的模板参数,该参数具有在正在进行参数推断的调用中使用的默认参数。
  •   

答案 1 :(得分:12)

总体而言,实现这一目标会有一些技术上的困难。请记住,模板中的默认参数在需要之前不会实例化。那么考虑一下:

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. 形成调用,实例化默认参数
  4. 为了从默认参数推断出,在完成扣减过程之前,必须自己实例化该默认参数。这可能会失败,导致SFINAE背景之外的错误。即,一个可能完全不适合呼叫的候选人可能会引发错误。

答案 2 :(得分: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);

即使写成一个,它仍然像一个中的两个功能,它们都不是&#34;首选&#34;无论如何。你正在调用单参数函数;两个参数实际上是一个不同的功能。默认参数表示法只是将它们合并为一个。

如果重载要具有您要求的行为,那么为了保持一致性,当模板分成两个定义时,它必须起作用。那是没有意义的,因为那么推论就是从一个没有被调用的无关函数中拉出类型!如果它没有实现,那就意味着重载不同的参数列表长度成为第二类公民&#34;与&#34;默认参数&#34;相比。

如果客户端完全隐藏了重载和默认值之间的差异,那就太好了。