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

Sam*_*rsa 18 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;
}
Run Code Online (Sandbox Code Playgroud)

调用a.bar<float>(5)解决了这个问题.为什么编译器不能从默认参数中推断出类型?

Jam*_*lis 18

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

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

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

template <typename T = float>
void bar(int a, T b = 0.0f) { }
Run Code Online (Sandbox Code Playgroud)

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

未推断的上下文是:

...

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

  • 虽然说"因为标准这么说"是一个有效的答案,但了解它背后的推理会很好. (13认同)
  • 除了默认模板参数之外的另一个解决方法是使用第二个函数,使用适当的默认`inline void bar(int a){bar(a,0.0f);}来调用第一个函数. (3认同)
  • 除其他原因外,函数的不同声明可以声明不同的默认参数(我很确定这同样适用于函数模板。) (2认同)
  • @James:不,不同的声明不允许声明不同的默认参数。甚至不允许多个声明为同一个参数提供相同的默认值。8.3.6 说“默认参数不应被后面的声明重新定义(甚至不能重新定义为相同的值)。” 当然,这只适用于非模板函数。对于模板函数,似乎只能在初始声明中提供默认参数。 (2认同)

Dav*_* V. 14

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

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);
Run Code Online (Sandbox Code Playgroud)

今天通过执行(除其他事项)以下步骤解决了这个问题:

  1. 试图推断候选人(A)和(B)的模板参数; 这对于(A)失败,因此被消除.
  2. 执行重载决策; (B)被选中
  3. 形成调用,实例化默认参数

为了从默认参数推导出,在完成推导过程之前,该默认参数必须自己实例化.这可能会失败,导致SFINAE背景之外的错误.即,可能完全不适合呼叫的候选者可能触发错误.