考虑以下代码:
#include <iostream>
template<class T>
void f(T& t)
{
t = T();
}
int main()
{
int x = 42;
f(x);
std::cout << x;
}
C++11 标准是否定义了输出应该是什么?我的编译器输出 0,但是我的印象是原始类型的默认构造函数是空操作或未定义的行为。
您的代码中不涉及“默认构造函数”。只有类类型可以有构造函数。标量类型没有默认或其他构造函数。
该T()
语法创建了一个由所谓的 value-initialization 初始化的临时对象。值初始化仅解析为类类型的构造函数调用,并且仅适用于具有用户定义构造函数的那些(在 C++11 中有一些细微差别)。对于其他类型,值初始化根本不涉及任何构造函数。它按照自己特定且相当复杂的初始化规则进行,这些初始化规则直接定义数据的初始值,而不涉及任何构造函数(参见语言规范中的 8.5)。
对于标量类型,值初始化执行零初始化。这就是为什么您的代码保证输出为零的原因。抽象初始化过程的确切细节在 C++ 语言标准的版本之间发生了变化,但是从一开始 C++ 语言就保证该T()
表达式的T == int
计算结果为零。即,即使在 C++98 中,您的代码也会输出零。
一种常见的误解是,所有这些T(...)
表达式都必然暗示构造函数调用。实际上,T(...)
表达式是一个函数转换表达式(不管参数的数量)(参见语言规范中的 5.2.3),它可能会在某些特定情况下解析为构造函数调用,并且与任何构造函数无关其他情况。
例如,这段代码
struct S { int x, y; };
S s = S();
尽管类具有默认构造函数,但保证s
用零(s.x
和)初始化。我专门提出了这个例子来说明这样一个事实,即即使在存在默认构造函数的情况下,表达式仍然可以完全忽略它并按照自己的规则工作。s.y
S
T()
以下是标准对您的问题的说明:
在8.5 中。第10段:
初始值设定项为空括号集的对象,即 (),应进行值初始化。
在8.5 中。第 7 段:
对 T 类型的对象进行值初始化意味着:
强调我的。所以,由于int
它甚至不是一个类类型,它属于最后一条规则,并且被零初始化,所以这是一个绝对正确的行为。