为什么C ++ 11不支持指定的初始化列表作为C99?
考虑:
struct Person { int height; int weight; int age; }; int main() { Person p { .age = 18 }; }
上面的代码在C99中是合法的,但在C ++ 11中不合法。
C ++ 11不支持这种方便的function的原因是什么?
C ++有构造函数。 如果初始化一个成员是有意义的,那么可以通过实现一个合适的构造函数来在程序中expression。 这是C ++推动的那种抽象。
另一方面,指定的初始值设定项function更多的是在客户端代码中直接访问和公开成员。 这导致像18岁(几岁),但身高和体重为零的人。
换句话说,指定的初始化器支持内部暴露的编程风格,客户可以灵活地决定他们想要如何使用这个types。
C ++更倾向于将灵活性放在typesdevise器的一面,所以devise者可以很容易地正确使用types,而且难以正确使用。 让devise者控制一个types如何被初始化是这样的:devise者确定构造函数,类内初始值设定项等等。
Boost实际上已经支持指定初始化程序,并且有很多提议支持c ++标准,例如: n4172和Daryle Walker的为初始化程序添加指定的build议 。 这些提案引用了c99在Visual C ++,gcc和Clang中指定的初始化程序的实现:
我们相信这些变化将会相对简单的执行
但标准委员会反复反对这样的build议 ,指出:
EWG发现了所提出的方法存在的各种问题,认为尝试解决问题并不可行,因为已经尝试了很多次,每次失败
本Voigt的评论帮助我看到这种方法不可逾越的问题; 给定:
struct X { int c; char a; float b; };
在c99中调用这些函数的顺序是什么: struct X foo = {.a = (char)f(), .b = g(), .c = h()}
? 令人惊讶的是,在c99 :
任何初始化子expression式的评估顺序是不确定的[ 1 ]
(Visual C ++, gcc和Clang似乎都有一致的行为,因为他们都会按照这个顺序进行调用:)
-
h()
-
f()
-
g()
但是标准的不确定性意味着如果这些函数有任何交互作用,结果程序状态也将是不确定的, 编译器不会警告你 : 有没有一种方法可以警告错误的指定的初始化器?
c ++ 确实有严格的初始化列表要求11.6.4 [dcl.init.list] 4:
在braced-init-list的初始化程序列表中,初始化程序子句(包括从程序包扩展(17.5.3)得到的任何结果子句)按其出现的顺序进行评估。 也就是说,与给定初始化子句相关联的每个值计算和副作用在与初始值设定项列表的逗号分隔列表中的任何初始化子句关联的每个值计算和副作用之前被sorting。
所以c ++支持需要按照以下顺序执行:
-
f()
-
g()
-
h()
打破与以前的c99实现的兼容性。 这里需要的是一个控制指定初始化器执行顺序的显式行为。 对核心工作组提出更有希望的build议是提出指定初始化的P0329R3 ,但具有以下限制:
[ 来源 ]
指定的初始化程序目前包含在C ++ 20工作主体中: http : //www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf所以我们最终可以看到它们!
两个核心C99function ,C + + 11缺乏提及“指定的初始化器和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(); }
我们知道在编译时, a_point.x
是零,所以我们可以预期foo
被优化成一个printf
。
$ gcc -O3 ac $ 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"
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(); }
这是优化汇编代码的输出。
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
我们可以看到, a_point
并不是一个真正的编译时间常量。