我很惊讶以下代码导致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)
可解决问题。为什么编译器不能从默认参数中推断出类型?
答案 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);
今天通过执行(除其他事项)以下步骤解决了这个问题:
为了从默认参数推断出,在完成扣减过程之前,必须自己实例化该默认参数。这可能会失败,导致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;相比。
如果客户端完全隐藏了重载和默认值之间的差异,那就太好了。