模板函数参数的默认值的数据类型与实例化数据类型不同

时间:2018-08-28 05:06:24

标签: c++ c++11 templates language-lawyer default-parameters

 #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类型。

3 个答案:

答案 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()实例。