作为this question的附录,这里发生了什么:
#include <string>
using namespace std;
struct A {
string s;
};
int main() {
A a = {0};
}
显然,你不能将std :: string设置为零。有人可以提供一个解释(请参考C ++标准,请参考)这里应该发生什么?然后解释一下):
int main() {
A a = {42};
}
这些都是明确定义的吗?
对我来说又一个令人尴尬的问题 - 我总是给我的结构构造者,所以这个问题从未出现过。
答案 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*
(如AndreyT和Johannes所示),
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 = 0
或std::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
。