C ++ 11이 지정된 초기화 목록을 C99로 지원하지 않는 이유는 무엇입니까? [닫은]
치다:
struct Person
{
int height;
int weight;
int age;
};
int main()
{
Person p { .age = 18 };
}
위의 코드는 C99에서는 유효하지만 C ++ 11에서는 유효하지 않습니다.
무엇 C ++ (11) 와 같은 편리한 기능에 대한 지원을 제외하고 표준위원회의 근거?
C ++에는 생성자가 있습니다. 하나의 멤버 만 초기화하는 것이 합리적이라면 적절한 생성자를 구현하여 프로그램에서 표현할 수 있습니다. 이것은 C ++ 프로모션의 일종입니다.
반면에 지정된 이니셜 라이저 기능은 멤버가 클라이언트 코드에서 직접 액세스하기 쉽고 노출시키는 것에 관한 것입니다. 이것은 18 세 (나이?)의 사람이 있지만 키와 몸무게가 0 인 사람을 낳습니다.
즉, 지정된 이니셜 라이저는 내부가 노출되는 프로그래밍 스타일을 지원하며 클라이언트는 유형을 사용하려는 방법을 유연하게 결정할 수 있습니다.
C ++은 대신 형식 디자이너 쪽에 유연성을 두는 데 더 관심이 있으므로 디자이너는 형식을 올바르게 사용하고 잘못 사용하기가 쉽지 않습니다. 디자이너가 형식을 초기화하는 방법을 제어하는 것은 이것의 일부입니다. 디자이너는 생성자, 클래스 내 이니셜 라이저 등을 결정합니다.
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; };
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 및 이전 버전 부스트 실제로이 지정 Intializers에 대한 지원을 하고 지원을 추가 할 수있는 많은 제안이 있었다 C ++ 예를 들어, 표준 : n4172 및 이니셜 라이저에 지정을 추가하는 Daryle Walker에의 제안 . 이 제안은 Visual C ++, gcc 및 Clang 클레임에서 c99 의 지정된 이니셜 라이저 구현을 인용 합니다.
변경 사항은 비교적 간단하게 구현할 수 있습니다.
그러나 표준위원회는 이러한 제안을 반복적으로 거부하며 다음과 같이 진술합니다.
EWG는 제안 된 접근 방식에서 다양한 문제를 발견했으며 여러 번 시도했지만 실패 할 때마다 문제를 해결하려고 시도하는 것이 불가능하다고 생각했습니다.
Ben Voigt의 의견 은이 접근 방식에서 극복 할 수없는 문제를 파악하는 데 도움이되었습니다. 주어진:
struct X {
int c;
char a;
float b;
};
이러한 기능에 어떤 순서를 호출 할 것이다 C99 : struct X foo = {.a = (char)f(), .b = g(), .c = h()}
? 놀랍게도 c99에서 :
이니셜 라이저에서 하위 표현식의 평가 순서는 불확실하게 시퀀싱됩니다 [ 1 ]
(Visual C ++, gcc 및 Clang은 모두 다음 순서로 호출하므로 동의 한 동작을하는 것 같습니다.)
h()
f()
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++ 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 c++ support would have required this to be executed in the order:
f()
g()
h()
Breaking compatibility with previous c99 implementations.
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.
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.x
0이라는 것을 알았 으므로 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
실제로 컴파일 타임 상수 값이 아니라는 것을 알 수 있습니다 .
'IT story' 카테고리의 다른 글
git add 실행 여부에 관계없이 분기 (수정, 추가, 삭제 된 파일)를 전환 할 때 왜 git에 변경 사항이 계속 표시됩니까? (0) | 2020.08.06 |
---|---|
json 매개 변수로 컬 GET 요청 (0) | 2020.08.06 |
SQL Server 설치-설치 미디어 폴더 란 무엇입니까? (0) | 2020.08.06 |
"필수"jQuery 플러그인은 무엇입니까? (0) | 2020.08.06 |
프로덕션 웹 응용 프로그램에 대한 초당 "평균"요청은 무엇입니까? (0) | 2020.08.06 |