C++ 在C+中初始化结构+;

C++ 在C+中初始化结构+;,c++,struct,C++,Struct,作为附录,这里发生了什么: #include <string> using namespace std; struct A { string s; }; int main() { A a = {0}; } 这两种定义是否明确 这又是一个让我尴尬的问题——我总是给我的结构构造函数,所以这个问题以前从未出现过。您的结构是一个聚合,因此聚合初始化的普通规则适用于它。该过程如8.5.1所述。基本上整个8.5.1都致力于此,所以我不认为有理由在这里复制整个内容。其基本思想

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

#include <string>
using namespace std;

struct A {
    string s;
};

int main() {
    A a = {0};
}
这两种定义是否明确

这又是一个让我尴尬的问题——我总是给我的结构构造函数,所以这个问题以前从未出现过。

您的结构是一个聚合,因此聚合初始化的普通规则适用于它。该过程如8.5.1所述。基本上整个8.5.1都致力于此,所以我不认为有理由在这里复制整个内容。其基本思想与C中的基本思想相同,只是适应了C++:从右边取一个初始值设定项,从左边取一个成员,然后用该初始值设定项初始化该成员。根据8.5/12,这应是一个副本初始化

当你这样做的时候

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

string s = 0;
上述编译是因为
std::string
可从
const char*
指针转换而来。(这是未定义的行为,因为在这种情况下,null指针不是有效的参数。)

您的
42
版本将无法编译,原因与

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

<强> P.S.以防万一:注意C++中的聚合定义不是递归的(与POD的定义相反)。code>std::string不是聚合,但它不会更改

A
的任何内容<代码>A仍然是一个聚合。

8.5.1/12“聚合”表示:

当使用初始值设定项列表中的初始值设定项初始化聚合成员时,将考虑所有隐式类型转换(第4条)

所以

将使用NULL
char*
进行初始化(如图所示),并且


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

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

#include <map>
using namespace std;

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

int main() {
    A a = {0};
}
#包括
使用名称空间std;
结构A{
地图m;
};
int main(){
A={0};
}

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

在21.3.1/9中,标准禁止
std::basic_字符串的相关构造函数的
char*
参数作为空指针。这应该会抛出一个
std::logic_错误
,但我还没有看到标准中的哪一个保证违反前提条件会抛出
std::logic_错误

0是一个空指针常量

S.4.9:

空指针常量是整数类型的整数常量表达式(5.19)右值,其计算结果为 零

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

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
const char*
的构造函数未定义为
显式
,这意味着您可以这样做:
std::string s=0

为了显示实际正在进行复制初始化,您可以执行以下简单测试:

class mystring
{
public:

  explicit mystring(const char* p){}
};

struct A {
  mystring s;
};


int main()
{
    //Won't compile because no implicit conversion exists from const char*
    //But simply take off explicit above and everything compiles fine.
    A a = {0};
    return 0;
}

类模板也是一个聚合。所以你可以做
数组a={“foo”,“bar”}使用它。另外,我的惰性构造数组也是一个聚合:隐式转换+聚合。。。ಠ_ಠ@litb当我第一次看到boost::array的特性时,我有了一个启示,也就是大脑的性满足感。非常有意义的简单事情往往会对我产生影响。§12.6.1也是相关的,如§8.5.1 13中所述。@outis:我查看了12.6.1,但无法立即看到它在8.5中已经添加了什么。每次12.6.1处理聚合初始化时,似乎都要回到8.5:)有趣的是,在
basic\u字符串(size\u type n,charT c,const Allocator a=Allocator())中,
有一个原因,为什么
size\u type n
没有默认值。原因是在指针和整数上重载是个坏主意。值0(零)为stric
A a = {42};
#include <map>
using namespace std;

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

int main() {
    A a = {0};
}
class mystring
{
public:

  explicit mystring(const char* p){}
};

struct A {
  mystring s;
};


int main()
{
    //Won't compile because no implicit conversion exists from const char*
    //But simply take off explicit above and everything compiles fine.
    A a = {0};
    return 0;
}