source

C++11은 C99로 지정된 이니셜라이저 목록을 지원하지 않는 이유는 무엇입니까?

goodcode 2022. 8. 24. 23:54
반응형

C++11은 C99로 지정된 이니셜라이저 목록을 지원하지 않는 이유는 무엇입니까?

고려사항:

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

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

위의 코드는 C99에서는 합법이지만 C++11에서는 합법적이지 않습니다.

c 표준위원회가 편리한 기능을 지원하지 않는 이유는 무엇이었습니까?

17년 7월 15일에 P0329R4는 c으로 승인되었습니다.http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf
c99의 Designated Initializer에 대한 지원이 제한됩니다.이 제한은 C.1.7[diff.decl]4에서 다음과 같이 기술되어 있습니다.

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

C에서 유효한 다음 Designated Initializations는 C++에서 제한됩니다.

  • struct A a = { .y = 1, .x = 2 }는 데이터 하기 때문에 C하지 않습니다.
  • int arr[3] = { [1] = 5 }의 designated 되지 않기 에 C입니다.
  • struct B b = {.a.x = 0} be 이기 때문에 입니다.
  • struct A c = {.x = 1, 2}는 모두 에 의해 C++에서는 무효입니다.

c 이전의 , 부스트는 실제로 Designated Initializer를 지원하며, n4172 Daryle Walker의 Initializer에 지정을 추가하는 제안등 c++ 표준에 대한 지원을 추가하는 제안이 다수 있었습니다.제안서에서는 다음과 같은 주장을 담은 의 Designated Initializer를 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()}놀랍게도 에서는:

이니셜라이저의 하위 표현식 평가 순서는 무한히 배열됩니다.

(Visual C++, gcc 및 Clang은 모두 다음 순서로 콜을 발신하기 때문에 동작에 합의된 것으로 보입니다).

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

그러나 이 표준의 불확정성은 이러한 함수에 어떠한 상호작용이 있을 경우 프로그램 상태도 불확정됨을 의미하며 컴파일러는 다음과 같이 경고하지 않습니다.Designated Initializer의 잘못된 동작에 대해 경고를 받을 수 있는 방법이 있습니까?

c에는 엄격한 이니셜라이저 리스트 요건이 11.6.4 [dcl.init.list]4:

bracked-init-list의 initializer-list 내에서 팩 확장(17.5.3)의 결과를 포함한 initializer-clauses는 나타나는 순서대로 평가된다.즉, initializer-clause와 관련된 모든 값 계산 및 사이드 ext는 initializer-list의 쉼표로 구분된 목록에서 그 뒤에 오는 initializer-clause와 관련된 모든 값 계산 및 부작용 전에 시퀀싱됩니다.

c 지원에서는 다음 순서로 실행해야 합니다.

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

이전의 실장과의 호환성이 깨지고 있습니다.
위에서 설명한 바와 같이 이 문제는 c에 수용되는 Designated Initializer 제한으로 회피되었습니다.또한 표준화된 동작을 제공하여 Designated Initializer의 실행 순서를 보장합니다.

약간의 해킹이 있어서 그냥 재미삼아 공유해요.

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

사용법은 다음과 같습니다.

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

다음과 같이 확장됩니다.

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

C++에는 컨스트럭터가 있습니다.멤버를 1개만 초기화하는 것이 타당하다면 적절한 컨스트럭터를 구현하여 프로그램에서 표현할 수 있습니다.이것은 C++가 추진하는 추상화의 일종입니다.

한편, 지정된 이니셜라이저 기능은 클라이언트 코드로 구성원에 직접 액세스하기 쉽게 하는 기능입니다.이는 키와 몸무게가 0인 18세(연령?)의 사람을 갖는 것과 같은 것으로 이어진다.


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

C++는, 타입의 디자이너의 측면에 유연성을 두는 것에 흥미가 있기 때문에, 디자이너는 타입을 올바르게 사용하기 쉽고, 잘못 사용하기 어렵게 됩니다.설계자가 생성자, 클래스 내 이니셜라이저를 결정하는 등 설계자에게 유형을 초기화하는 방법을 제어하는 것도 이 작업의 일부입니다.

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!

C++11에는 없는2개의 핵심 C99 기능에는 "Designated Initializer 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는 컴파일 시간 상수 값이 아닙니다.

언급URL : https://stackoverflow.com/questions/18731707/why-does-c11-not-support-designated-initializer-lists-as-c99

반응형