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

Data type of default value for template function argument different from instantiating data type

 #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
 }

Case 1和Case 3不应该也失败吗,因为函数模板是由int实例化的,默认值是std::string.

类型

T x = std::string{""} 仅在 x.

没有给出参数时执行
f(23)

隐式实例化到 f<int>。未使用 x 的默认值,因为 23 作为 int 文字提供。

f<int>()

现在 Tint 类型,但您正在使用 std::string

分配给 x
f<int>(23)

T 仍然是 int,与情况 1

相同

Shouldn't the Case 1 and Case 3 also fail, because the function template is instantiated by int and the deafult value is of type std::string?

不应该。原因见下文。如果您希望它失败,那么您应该将 f() 定义为 常规函数 接收类型 std::string 而不是 函数模板.

你有一个默认参数实例化的例子,见[temp.inst]:

If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation [...] is done as if the default argument had been an initializer used in a function template specialization, [...] This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.

当你写:

template <typename T>
void f(T x = std::string{""}){...}

这意味着 ""x 的默认参数 仅在调用不带任何参数的情况下 。例如在您的 案例 2.

您的模板函数被定义为可以很好地用于 int 或各种其他 类型 .

例如这里(案例1):

f(23);

隐式(根据参数类型推断)为f<int>(),因为23在规范中定义为int 文字int 类型的参数 x 接收您在调用站点提供的 23 非默认值

这里 (案例 2)

f<int>();

正是上述标准条款发挥作用的地方:您成功实例化 f()但没有提供参数它,因此 然后将实例化的默认参数用作 f 的参数。因此编译失败,因为这里参数 x 默认值 被定义为 std::string 并且没有从它应用到类型 int 的转换。 它实际上等同于在声明为 void f(int x).

的函数上调用 f("")

这里 (案例 3)

f<int>(23);

再次显式,这次您在调用站点提供了正确类型的参数。

@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() 实例化。