模板函数参数的默认值数据类型与实例化数据类型不同
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>()
现在 T
是 int
类型,但您正在使用 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()
实例化。
#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>()
现在 T
是 int
类型,但您正在使用 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 off
.
当你写:
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()
实例化。