xml*_*lmx 110 c c++ initialization c99 c++11
考虑:
struct Person
{
int height;
int weight;
int age;
};
int main()
{
Person p { .age = 18 };
}
Run Code Online (Sandbox Code Playgroud)
上面的代码在C99中是合法的,但在C++ 11中不合法.
什么是C++ 11标准委员会排除了这样一个方便的功能支持的理由?
Jon*_*Mee 78
7月15日'17 P0329R4被接纳进入C++ 20标准:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf
这带来了有限的支持C99 " s指定的初始化器.该限制由C.1.7 [diff.decl] .4描述如下:
struct A { int x, y; };
struct B { struct A a; };
Run Code Online (Sandbox Code Playgroud)
以下指定的初始化(在C中有效)在C++中受到限制:
struct A a = { .y = 1, .x = 2 }
在C++中无效,因为指示符必须以数据成员的声明顺序出现int arr[3] = { [1] = 5 }
在C++中无效,因为不支持数组指定的初始化struct B b = {.a.x = 0}
在C++中无效,因为指定符不能嵌套struct A c = {.x = 1, 2}
在C++中无效,因为所有数据成员都必须由指定者初始化对于c ++ 17及更早版本,Boost实际上支持指定的Iniantizers,并且已经提出了许多增加对c ++标准的支持的建议,例如:n4172和Daryle Walker为初始化器添加指定的提议.这些提议引用了C99的Visual C++,gcc和Clang中的指定初始化器的实现声明:
我们相信这些变化实施起来相对简单
但标准委员会一再拒绝此类提案,并指出:
EWG发现了提出的方法存在各种问题,并且认为尝试解决问题并不可行,因为它已经多次尝试并且每次都失败了
Ben Voigt的评论帮助我看到了这种方法难以克服的问题; 给定:
struct X {
int c;
char a;
float b;
};
Run Code Online (Sandbox Code Playgroud)
什么样的顺序将这些功能堪称C99:struct X foo = {.a = (char)f(), .b = g(), .c = h()}
?令人惊讶的是,在c99中:
任何初始化程序中子表达式的评估顺序是不确定的顺序[ 1 ]
(Visual C++,gcc和Clang似乎有一个约定的行为,因为他们都将按此顺序调用:)
h()
f()
g()
但是标准的不确定性意味着如果这些函数有任何交互,最终的程序状态也将是不确定的,编译器不会警告你:有没有办法得到警告有关行为不端的指定初始化器?
c ++ 确实有严格的初始化列表要求11.6.4 [dcl.init.list] 4:
在braced-init-list的initializer-list中,initializer-clauses(包括pack扩展(17.5.3)产生的任何结果)按照它们出现的顺序进行评估.也就是说,与给定初始化子句相关联的每个值计算和副作用在每个值计算和与初始化列表的逗号分隔列表中跟随它之后的任何初始化子句相关联的副作用之前被排序.
所以c ++支持需要按顺序执行:
f()
g()
h()
打破与以前的c99实现的兼容性.
如上所述,这个问题已经被c ++ 20中接受的指定初始化器的限制所规避.它们提供标准化行为,保证指定初始化程序的执行顺序.
bam*_*s53 31
C++有构造函数.如果只初始化一个成员是有意义的,那么可以通过实现适当的构造函数在程序中表达.这是C++推广的一种抽象.
另一方面,指定的初始化器功能更多地是关于公开并使成员易于直接在客户端代码中访问.这会导致像18岁(年?)但身高和体重为零的人.
换句话说,指定的初始化器支持一种内部暴露的编程风格,并且客户端可以灵活地决定他们想要如何使用该类型.
C++更感兴趣的是将灵活性放在类型设计器的一边,因此设计人员可以很容易地正确使用类型并且难以正确使用.让设计者控制如何初始化类型是其中的一部分:设计者确定构造函数,类内初始化器等.
kee*_*bus 26
有点hackery,所以只是分享乐趣.
#define with(T, ...)\
([&]{ T ${}; __VA_ARGS__; return $; }())
Run Code Online (Sandbox Code Playgroud)
并使用它像:
MyFunction(with(Params,
$.Name = "Foo Bar",
$.Age = 18
));
Run Code Online (Sandbox Code Playgroud)
扩展到:
MyFunction(([&] {
Params ${};
$.Name = "Foo Bar", $.Age = 18;
return $;
}()));
Run Code Online (Sandbox Code Playgroud)
Ser*_*eyA 22
指定初始化程序目前包含在C++ 20工作主体中:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf所以我们可能最终看到它们!
C++ 11缺乏的两个核心C99功能提到"指定的初始化器和C++".
我认为'指定的初始化程序'与潜在的优化有关.这里我以"gcc/g ++"5.1为例.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
struct point {
int x;
int y;
};
const struct point a_point = {.x = 0, .y = 0};
int foo() {
if(a_point.x == 0){
printf("x == 0");
return 0;
}else{
printf("x == 1");
return 1;
}
}
int main(int argc, char *argv[])
{
return foo();
}
Run Code Online (Sandbox Code Playgroud)
我们在编译时知道,它a_point.x
是零,所以我们可以预期它会foo
被优化为单个printf
.
$ gcc -O3 a.c
$ gdb a.out
(gdb) disassemble foo
Dump of assembler code for function foo:
0x00000000004004f0 <+0>: sub $0x8,%rsp
0x00000000004004f4 <+4>: mov $0x4005bc,%edi
0x00000000004004f9 <+9>: xor %eax,%eax
0x00000000004004fb <+11>: callq 0x4003a0 <printf@plt>
0x0000000000400500 <+16>: xor %eax,%eax
0x0000000000400502 <+18>: add $0x8,%rsp
0x0000000000400506 <+22>: retq
End of assembler dump.
(gdb) x /s 0x4005bc
0x4005bc: "x == 0"
Run Code Online (Sandbox Code Playgroud)
foo
优化x == 0
仅打印.
对于C++版本,
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
struct point {
point(int _x,int _y):x(_x),y(_y){}
int x;
int y;
};
const struct point a_point(0,0);
int foo() {
if(a_point.x == 0){
printf("x == 0");
return 0;
}else{
printf("x == 1");
return 1;
}
}
int main(int argc, char *argv[])
{
return foo();
}
Run Code Online (Sandbox Code Playgroud)
这是优化汇编代码的输出.
g++ -O3 a.cc
$ gdb a.out
(gdb) disassemble foo
Dump of assembler code for function _Z3foov:
0x00000000004005c0 <+0>: push %rbx
0x00000000004005c1 <+1>: mov 0x200489(%rip),%ebx # 0x600a50 <_ZL7a_point>
0x00000000004005c7 <+7>: test %ebx,%ebx
0x00000000004005c9 <+9>: je 0x4005e0 <_Z3foov+32>
0x00000000004005cb <+11>: mov $0x1,%ebx
0x00000000004005d0 <+16>: mov $0x4006a3,%edi
0x00000000004005d5 <+21>: xor %eax,%eax
0x00000000004005d7 <+23>: callq 0x400460 <printf@plt>
0x00000000004005dc <+28>: mov %ebx,%eax
0x00000000004005de <+30>: pop %rbx
0x00000000004005df <+31>: retq
0x00000000004005e0 <+32>: mov $0x40069c,%edi
0x00000000004005e5 <+37>: xor %eax,%eax
0x00000000004005e7 <+39>: callq 0x400460 <printf@plt>
0x00000000004005ec <+44>: mov %ebx,%eax
0x00000000004005ee <+46>: pop %rbx
0x00000000004005ef <+47>: retq
Run Code Online (Sandbox Code Playgroud)
我们可以看到它a_point
实际上不是编译时常量值.