为什么 C++11 不支持像 C99 那样的指定初始化列表?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18731707/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:10:43  来源:igfitidea点击:

Why does C++11 not support designated initializer lists as C99?

c++cc++11initializationc99

提问by xmllmx

Consider:

考虑:

struct Person
{
    int height;
    int weight;
    int age;
};

int main()
{
    Person p { .age = 18 };
}

The code above is legal in C99, but not legal in C++11.

上面的代码在 C99 中合法,但在 C++11 中不合法。

What was the c++11standard committee's rationale for excluding support for such a handy feature?

什么是C ++ 11标准委员会排除了这样一个方便的功能支持的理由?

采纳答案by bames53

C++ has constructors. If it makes sense to initialize just one member then that can be expressed in the program by implementing an appropriate constructor. This is the sort of abstraction C++ promotes.

C++ 有构造函数。如果只初始化一个成员有意义,那么可以通过实现适当的构造函数在程序中表达。这是 C++ 提倡的那种抽象。

On the other hand the designated initializers feature is more about exposing and making members easy to access directly in client code. This leads to things like having a person of age 18 (years?) but with height and weight of zero.

另一方面,指定初始值设定项功能更多地是为了在客户端代码中直接公开成员并使成员易于访问。这导致诸如拥有一个 18 岁(年?)但身高和体重为零的人。



In other words, designated initializers support a programming style where internals are exposed, and the client is given flexibility to decide how they want to use the type.

换句话说,指定的初始值设定项支持内部公开的编程风格,并且客户端可以灵活地决定他们想要如何使用类型。

C++ is more interested in putting the flexibility on the side of the designerof a type instead, so designers can make it easy to use a type correctly and difficult to use incorrectly. Putting the designer in control of how a type can be initialized is part of this: the designer determines constructors, in-class initializers, etc.

C++ 更感兴趣的是将灵活性放在类型的设计者一边,因此设计者可以轻松正确地使用类型,并且难以错误地使用类型。让设计者控制如何初始化类型是其中的一部分:设计者确定构造函数、类内初始化器等。

回答by Jonathan Mee

On July 15 '17 P0329R4was accepted into the c++20standard: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf
This brings limited support for c99's Designated Initializers. This limitation is described as follows by C.1.7[diff.decl].4, given:

7月15日'17 P0329R4被接纳进入C ++ 20标准:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf
这带来了有限的支持C99“ ■ 指定的初始化程序。此限制在 C.1.7[diff.decl].4 中描述如下,给出:

struct A { int x, y; };
struct B { struct A a; };

The following Designated Initializations, which are valid in C, are restricted in C++:

以下在 C 中有效的指定初始化在 C++ 中受到限制:

  • struct A a = { .y = 1, .x = 2 }is invalid in C++ because designators must appear in the declaration order of the data members
  • int arr[3] = { [1] = 5 }is invalid in C++ because array designated initialization is not supported
  • struct B b = {.a.x = 0}is invalid in C++ because designators cannot be nested
  • struct A c = {.x = 1, 2}is invalid in C++ because either all or none of the data members must be initialized by designators
  • 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++ 中无效,因为所有或没有数据成员必须由指示符初始化


For c++17and earlier Boost actually has support for Designated Intializersand there have been numerous proposals to add support to the c++standard, for example: n4172and Daryle Walker's Proposal to Add Designation to Initializers. The proposals cite implementation of c99's Designated Initializers in Visual C++, gcc, and Clang claiming:

对于c++17及更早版本,Boost 实际上支持指定的初始化器,并且已经有许多提议来添加对c++标准的支持,例如:n4172Daryle Walker 的建议将指定添加到初始化器。这些提案引用了 Visual C++、gcc 和 Clang中c99的指定初始值设定项的实现,声称:

We believe the changes will be relatively straightforward to implement

我们相信这些变化的实施将相对简单

But the standard committee repeatedly rejects such proposals, stating:

但标准委员会一再拒绝这样的提议,并指出:

EWG found various problems with the proposed approach, and didn't think it's feasible to try solving the problem, as it has been tried many times and every time it has failed

EWG 发现所提出的方法存在各种问题,并认为尝试解决该问题不可行,因为已经尝试了很多次,但每次都失败了

Ben Voigt's commentshave helped me to see the insurmountable problems with this approach; given:

Ben Voigt 的评论帮助我看到了这种方法无法解决的问题;给出:

struct X {
    int c;
    char a;
    float b;
};

What order would these functions be called in in c99: struct X foo = {.a = (char)f(), .b = g(), .c = h()}? Surprisingly, in c99:

c99 中这些函数的调用顺序是什么:struct X foo = {.a = (char)f(), .b = g(), .c = h()}?令人惊讶的是,在c99 中

The order of evaluation of the subexpressions in any initializer is indeterminately sequenced [1]

任何初始化器中子表达式的求值顺序都是不确定的[ 1]

(Visual C++, gcc, and Clang seem to have an agreed upon behavior as they will all make the calls in this order:)

(Visual C++、gcc和 Clang 似乎有一致的行为,因为它们都将按此顺序进行调用:)

  1. h()
  2. f()
  3. g()
  1. h()
  2. f()
  3. g()

But the indeterminate nature of the standard means that if these functions had any interaction the resulting program state would also be indeterminate, and the compiler wouldn't warn you: Is there a Way to Get Warned about Misbehaving Designated Initializers?

但是标准的不确定性意味着如果这些函数有任何交互,结果程序状态也将是不确定的,并且编译器不会警告你有没有办法得到关于行为不端的指定初始化程序的警告?

c++doeshave stringent initializer-list requirements 11.6.4[dcl.init.list]4:

c++确实有严格的初始化列表要求 11.6.4[dcl.init.list]4:

Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (17.5.3), are evaluated in the order in which they appear. That is, every value computation and side e?ect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list.

在花括号初始化器列表的初始化器列表中,初始化器子句,包括由包扩展(17.5.3)产生的任何结果,按它们出现的顺序进行评估。也就是说,与给定的初始化子句相关联的每个值计算和副作用都在与在初始化器列表的逗号分隔列表中跟随它的任何初始化子句相关联的每个值计算和副作用之前排序。

So c++support would have required this to be executed in the order:

因此,c++支持将要求按以下顺序执行:

  1. f()
  2. g()
  3. h()
  1. f()
  2. g()
  3. h()

Breaking compatibility with previous c99implementations.
As discussed above, this issue has been circumvented by the limitations on Designated Initializers accepted into c++20. They provide a standardized behavior, guaranteeing the execution order of Designated Initializers.

打破与以前的c99实现的兼容性。
如上所述,这个问题已经被c++20接受的指定初始化器的限制规避了。它们提供标准化的行为,保证指定初始化程序的执行顺序。

回答by keebus

A bit of hackery, so just sharing for fun.

有点hacky,所以只是为了好玩而分享。

#define with(T, ...)\
    ([&]{ T ${}; __VA_ARGS__; return $; }())

And use it like:

并像这样使用它:

MyFunction(with(Params,
    $.Name = "Foo Bar",
    $.Age  = 18
));

which expands to:

扩展为:

MyFunction(([&] {
 Params ${};
 $.Name = "Foo Bar", $.Age = 18;
 return $;
}()));

回答by SergeyA

Designated initializer are currently included in C++20 body of work: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdfso we might finally see them!

指定的初始化程序目前包含在 C++20 工作主体中:http: //www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf所以我们可能最终会看到它们!

回答by wcy

Two Core C99 Featuresthat C++11 Lacks mentions “Designated Initializers and C++”.

C++11 缺乏的两个 C99 核心特性提到了“指定的初始化器和 C++”。

I think the ‘designated initializer' related with potential optimization. Here I use “gcc/g++” 5.1 as an example.

我认为“指定初始值设定项”与潜在优化有关。这里我以“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();
}

We knew at compilation time, a_point.xis zero, so we could expected that foois optimized into a single printf.

我们在编译时就知道,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    
#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();
}
x8,%rsp 0x00000000004004f4 <+4>: mov
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    ##代码##x1,%ebx
0x00000000004005d0 <+16>:   mov    ##代码##x4006a3,%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    ##代码##x40069c,%edi
0x00000000004005e5 <+37>:   xor    %eax,%eax
0x00000000004005e7 <+39>:   callq  0x400460 <printf@plt>
0x00000000004005ec <+44>:   mov    %ebx,%eax
0x00000000004005ee <+46>:   pop    %rbx
0x00000000004005ef <+47>:   retq   
x4005bc,%edi 0x00000000004004f9 <+9>: xor %eax,%eax 0x00000000004004fb <+11>: callq 0x4003a0 <printf@plt> 0x0000000000400500 <+16>: xor %eax,%eax 0x0000000000400502 <+18>: add ##代码##x8,%rsp 0x0000000000400506 <+22>: retq End of assembler dump. (gdb) x /s 0x4005bc 0x4005bc: "x == 0"

foois optimized to print x == 0only.

foo优化为x == 0仅打印。

For C++ version,

对于 C++ 版本,

##代码##

And this is output of the optimized assemble code.

这是优化汇编代码的输出。

##代码##

We can see that a_pointis not really a compile time constant value.

我们可以看到这a_point并不是真正的编译时常量值。