在C ++中初始化结构

时间:2010-05-14 22:45:49

标签: c++ struct

作为this question的附录,这里发生了什么:

#include <string>
using namespace std;

struct A {
    string s;
};

int main() {
    A a = {0};
}

显然,你不能将std :: string设置为零。有人可以提供一个解释(请参考C ++标准,请参考)这里应该发生什么?然后解释一下):

int main() {
    A a = {42};
}

这些都是明确定义的吗?

对我来说又一个令人尴尬的问题 - 我总是给我的结构构造者,所以这个问题从未出现过。

5 个答案:

答案 0 :(得分:29)

您的struct是聚合,因此聚合初始化的普通规则适用于它。该过程在8.5.1中描述。基本上整个8.5.1专用于它,所以我没有看到在这里复制整个事情的原因。一般的想法几乎与C中的一样,只适用于C ++:你从右边拿一个初始化器,你从左边拿一个成员,然后用初始化器初始化成员。根据8.5 / 12,这应该是复制初始化

当你这样做时

A a = { 0 };

您基本上是使用a.s复制初始化0,即对于a.s,它在语义上等同于

string s = 0;

以上编译是因为std::string可以从const char *指针转换。 (并且它是未定义的行为,因为在这种情况下空指针不是有效的参数。)

由于

的原因,您的42版本无法编译
string s = 42;

不会编译。 42不是空指针常量,std::string无法从int类型转换。

P.S。以防万一:请注意,C ++中聚合的定义不是递归的(例如,与POD的定义相反)。 std::string不是聚合,但它不会改变A的任何内容。 A仍然是一个聚合。

答案 1 :(得分:8)

8.5.1 / 12“聚合”说:

  

使用初始化列表初始化器初始化聚合成员时,会考虑所有隐式类型转换(第4节)。

所以

A a = {0};

将初始化为NULL char*(如AndreyTJohannes所示),

A a = {42};

将在编译时失败,因为没有与std::string构造函数匹配的隐式转换。

答案 2 :(得分:3)

0是空指针常量

S.4.9:

  

空指针常量是求值为的整数类型的整数常量表达式(5.19)rvalue   零。

空指针常量可以转换为任何其他指针类型:

S.4.9:

  

空指针常量可以转换为指针类型;结果是该指针的空指针值   型

您为A的定义提供的内容被视为聚合:

S.8.5.1:

  

聚合是一个数组或类,没有用户声明的构造函数,没有私有或受保护   非静态数据成员,没有基类,也没有虚函数。

您正在指定初始化子句:

S.8.5.1:

  

初始化聚合时,初始化程序可以包含一个初始化子句,该子句包含一个括号,   逗号分隔的聚合成员的初始化子句列表

A包含类型为std::string的聚合的成员,并且初始化子句适用于它。

您的汇总已复制初始化

  

当聚合(无论是类还是数组)包含类类型的成员并由括号括起来初始化时   初始化列表,每个这样的成员都是复制初始化的。

复制初始化意味着您拥有等效于std::string s = 0std::string s = 42;

S.8.5-12

  

在参数传递,函数返回,抛出异常(15.1),处理时发生的初始化   异常(15.3)和大括号括起的初始化列表(8.5.1)称为复制初始化并且是等效的   形式为T x = a;

std::string s = 42将无法编译,因为没有隐式转换,std::string s = 0将编译(因为存在隐式转换)但会导致未定义的行为。

std::string的{​​{1}}构造函数未定义为const char*,这意味着您可以执行此操作:explicit

只是为了表明事情实际上是复制初始化的,你可以做这个简单的测试:

std::string s = 0

答案 3 :(得分:2)

正如人们所指出的,这个“有效”,因为string有一个构造函数,可以将0作为参数。如果我们说:

#include <map>
using namespace std;

struct A {
    map <int,int> m;
};

int main() {
    A a = {0};
}

然后我们得到一个编译错误,因为map类没有这样的构造函数。

答案 4 :(得分:1)

在21.3.1 / 9中,标准禁止char*的相关构造函数的std::basic_string参数为空指针。这应该抛出一个std::logic_error,但我还没有看到标准中的哪个位置保证违反先决条件会引发std::logic_error