#include <iostream>
#include <string>
template <typename T>
void f(T x = std::string{""})
{
std::cout << '[' << x << ']' << std::endl;
}
int main()
{
f(23); // Case 1: Doesn't fail
f<int>(); // Case 2: Compilation error
f<int>(23); // Case 3: Doesn't fail
}
情况1和情况3也不应该失败,因为功能模板由int
实例化并且默认值是std::string
类型。
答案 0 :(得分:4)
T x = std::string{""}
仅在没有为x
给出arg的情况下执行。
f(23)
隐式实例化到f<int>
。未使用x
的默认值,因为23
作为int
文字提供。
f<int>()
现在T
的类型为int
,但是您要使用x
分配给std::string
f<int>(23)
T
仍为int
,与情况1相同
答案 1 :(得分:3)
案例1和案例3也不应该失败,因为函数 模板由int实例化,默认值的类型为 std :: string?
不,不应该。有关原因,请参见下文。如果您希望它失败,则应将f()
定义为常规函数,它接收类型为std::string
的参数,而不是函数模板。
您遇到的是 默认参数实例化 的情况,请参见[temp.inst]此处:
如果以要求默认值的方式调用功能模板
f
要使用的参数,查找从属名称,语义 检查约束,然后完成实例化[...],就好像默认参数是一个 功能模板专门化中使用的初始化程序,[...] 称为默认参数实例化。 实例化的默认值 然后将实参用作f
的实参。
写时:
template <typename T>
void f(T x = std::string{""}){...}
这意味着""
是x
的默认参数,仅在调用时不带任何参数的情况下。例如在您的案例2 中。
您的模板函数被定义为对于int
或其他各种类型来说都是正确的。
例如此处(案例1):
f(23);
(根据参数类型进行推断)被隐式设置为f<int>()
,因为规范中将23
定义为 {{ 1}}文字。类型为int
的参数x
接收您在呼叫站点提供的int
的非默认值。
这里是(案例2):
23
正是上述标准子句起作用的地方:您成功地实例化了 f<int>();
,但没有提供任何参数,因此实例化了然后,将默认参数用作f()
的参数。因此编译失败,因为此处参数f
的默认值被定义为x
,并且没有转换适用于类型std::string
。 这实际上等效于在声明为int
的函数上调用f("")
。
这里是(案例3):
void f(int x)
再次明确,这次您在呼叫站点提供了正确的参数类型。
答案 2 :(得分:0)
@Abigail提供的答案说明了为什么情况1和3不会失败,让我另外指出,函数模板没有那么大的意义。具有一个具有默认值的参数的函数可以像这样被调用:
void g(std::string = "") { /* ... */ }
g(); // Use default parameter
g("non-default");
相反,可以使用给定参数调用函数模板
f("non-default");
但并非没有任何参数,因为编译器不会从默认参数中推断出模板类型。
f(); // Doesn't compile
我建议将模板更改为
template <typename T = std::string>
void f(T x = T{})
{
// same as before
}
可修复f()
实例。