IT story

C ++ 11이 지정된 초기화 목록을 C99로 지원하지 않는 이유는 무엇입니까?

hot-time 2020. 8. 6. 07:52
반응형

C ++ 11이 지정된 초기화 목록을 C99로 지원하지 않는 이유는 무엇입니까? [닫은]


치다:

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

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

위의 코드는 C99에서는 유효하지만 C ++ 11에서는 유효하지 않습니다.

무엇 와 같은 편리한 기능에 대한 지원을 제외하고 표준위원회의 근거?


C ++에는 생성자가 있습니다. 하나의 멤버 만 초기화하는 것이 합리적이라면 적절한 생성자를 구현하여 프로그램에서 표현할 수 있습니다. 이것은 C ++ 프로모션의 일종입니다.

반면에 지정된 이니셜 라이저 기능은 멤버가 클라이언트 코드에서 직접 액세스하기 쉽고 노출시키는 것에 관한 것입니다. 이것은 18 세 (나이?)의 사람이 있지만 키와 몸무게가 0 인 사람을 낳습니다.


즉, 지정된 이니셜 라이저는 내부가 노출되는 프로그래밍 스타일을 지원하며 클라이언트는 유형을 사용하려는 방법을 유연하게 결정할 수 있습니다.

C ++은 대신 형식 디자이너 쪽에 유연성을 두는 데 더 관심이 있으므로 디자이너는 형식을 올바르게 사용하고 잘못 사용하기가 쉽지 않습니다. 디자이너가 형식을 초기화하는 방법을 제어하는 ​​것은 이것의 일부입니다. 디자이너는 생성자, 클래스 내 이니셜 라이저 등을 결정합니다.


7 월 15 일 '17 P0329R4는 으로 받아 들여졌다 : 표준 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf
이가 제한된 지원을 제공합니다 ' 지정된 이니셜 라이저. 이 제한은 다음과 같이 C.1.7 [diff.decl] .4로 설명됩니다.

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

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 ++에서는 지정자가 데이터 멤버를 모두 초기화하거나 초기화하지 않아야하므로 유효하지 않습니다.

를 들어 및 이전 버전 부스트 실제로이 지정 Intializers에 대한 지원을 하고 지원을 추가 할 수있는 많은 제안이 있었다 예를 들어, 표준 : n4172이니셜 라이저에 지정을 추가하는 Daryle Walker에의 제안 . 이 제안은 Visual C ++, gcc 및 Clang 클레임에서 의 지정된 이니셜 라이저 구현을 인용 합니다.

변경 사항은 비교적 간단하게 구현할 수 있습니다.

그러나 표준위원회는 이러한 제안을 반복적으로 거부하며 다음과 같이 진술합니다.

EWG는 제안 된 접근 방식에서 다양한 문제를 발견했으며 여러 번 시도했지만 실패 할 때마다 문제를 해결하려고 시도하는 것이 불가능하다고 생각했습니다.

Ben Voigt의 의견 은이 접근 방식에서 극복 할 수없는 문제를 파악하는 데 도움이되었습니다. 주어진:

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

이러한 기능에 어떤 순서를 호출 할 것이다 : struct X foo = {.a = (char)f(), .b = g(), .c = h()}? 놀랍게도 :

이니셜 라이저에서 하위 표현식의 평가 순서는 불확실하게 시퀀싱됩니다 [ 1 ]

(Visual C ++, gcc 및 Clang은 모두 다음 순서로 호출하므로 동의 한 동작을하는 것 같습니다.)

  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?

does have stringent initializer-list requirements 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 effect 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.

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

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

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


A bit of hackery, so just sharing for fun.

#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 $;
}()));

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


Two Core C99 Features that C++11 Lacks mentions “Designated Initializers and 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.x0이라는 것을 알았 으므로 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"

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실제로 컴파일 타임 상수 값이 아니라는 것을 알 수 있습니다 .

참고 : https://stackoverflow.com/questions/18731707/why-does-c11-not-support-designated-initializer-lists-as-c99

반응형