다음 세 가지를 고려하십시오 struct
.
class blub {
int i;
char c;
blub(const blub&) {}
};
class blob {
char s;
blob(const blob&) {}
};
struct bla {
blub b0;
blob b1;
};
int
4 바이트 인 일반 플랫폼에서 크기, 정렬 및 총 패딩 1 은 다음과 같습니다.
struct size alignment padding
-------- ------ ----------- ---------
blub 8 4 3
blob 1 1 0
bla 12 4 6
크기 1 이 원칙적으로 패딩에 “적합”할 수 있지만, 부재 blub
와 blob
부재 의 보관 사이에는 겹침이 없다 .blob
blub
C ++ 20은 no_unique_address
속성을 도입하여 인접한 빈 멤버가 동일한 주소를 공유 할 수 있도록합니다. 또한 한 멤버의 패딩을 사용하여 다른 멤버를 저장하는 위에서 설명한 시나리오를 명시 적으로 허용합니다. 에서 cppreference (강조 광산) :
이 데이터 멤버가 해당 클래스의 다른 모든 비 정적 데이터 멤버와 다른 주소를 가질 필요는 없음을 나타냅니다. 즉, 멤버에 빈 유형 (예 : 상태 비 저장 할당 자)이 있으면 컴파일러는 빈베이스 인 것처럼 공간을 차지하지 않도록 최적화 할 수 있습니다. 멤버가 비어 있지 않은 경우 다른 데이터 멤버를 저장하기 위해 테일 패딩을 재사용 할 수도 있습니다.
실제로이 속성을 on으로 사용하면 drop blub b0
의 크기가 bla
로 변경 8
되므로 godbolt에 표시된대로blob
실제로 저장됩니다 .blub
마지막으로, 우리는 내 질문에 도달합니다.
no_unique_address
사소하게 복사 할 수없는 객체 에 대해 표준의 어떤 텍스트 (C ++ 11에서 C ++ 20까지)로이 겹침을 방지 할 수 있습니까?
TC 객체의 std::memcpy
경우 멤버 하위 객체를 포함하여 한 객체에서 다른 객체로 이동할 수 있으므로 스토리지가 겹치면 스토리지의 전체 또는 일부가 손상되므로 TC 객체를 위에서 제외해야합니다. 인접한 멤버를 덮어 씁니다.) 2 .
1 패딩은 단순히 구조 크기와 모든 구성 멤버의 크기 차이를 재귀 적으로 계산합니다.
2 할 : 나는 복사 생성자가 정의한 이유입니다 blub
및 blob
하지 하찮게 복사 가능한 .
답변
표준은 메모리 모델에 대해 말할 때 매우 조용하고 사용되는 용어 중 일부에 대해서는 명확하지 않습니다. 그러나 나는 일하는 논쟁을 발견했다고 생각합니다 (약간 약할 수 있습니다)
먼저, 물체의 일부가 무엇인지 알아 봅시다. [기본 유형] / 4 :
타입의 오브젝트의 오브젝트 표현
T
들의 시퀀스이며N
unsigned char
유형의 오브젝트가 차지하는 오브젝트T
,N
같음sizeof(T)
. type 객체의 값 표현은 typeT
값을 나타내는 데 참여하는 비트 세트입니다T
. 값 표현의 일부가 아닌 객체 표현의 비트는 패딩 비트입니다.
따라서 객체 표현은 객체로 b0
구성 sizeof(blub)
unsigned char
되므로 8 바이트입니다. 패딩 비트는 객체의 일부입니다.
[basic.life] /1.5 안에 중첩되어 있지 않은 객체는 다른 객체의 공간을 차지할 수 없습니다 .
o
유형 의 객체 수명은 다음과 같은T
경우에 종료됩니다.[…]
(1.5) 객체가 차지하는 스토리지가 해제되거나
o
([intro.object]) 내에 중첩되지 않은 객체가 재사용합니다 .
따라서 b0
스토리지가 차지하는 스토리지가 다른 오브젝트 (예 :)에서 재사용 될 때 수명 이 끝납니다 b1
. 나는 그것을 확인하지는 않았지만 살아있는 객체의 하위 객체도 살아 있어야한다고 표준이 규정하고 있다고 생각합니다 (그리고 이것이 어떻게 다르게 작동하는지 상상할 수 없었습니다).
저장 그래서 b0
점유 에 의해 사용되지 않을 수 있습니다 b1
. 표준에서 “점유”에 대한 정의를 찾지 못했지만 합리적인 해석은 “객체 표현의 일부”라고 생각합니다. 인용 부호를 나타내는 객체 표현에서 “take up”이라는 단어가 사용됩니다 1 . 여기서는 8 바이트이므로 bla
에 대해 하나 이상이 필요 b1
합니다.
특히 하위 객체 (정적이 아닌 데이터 멤버)의 경우 [intro.object] / 9 규정이 있습니다 (그러나 이것은 C ++ 20, thx @BeeOnRope에 추가되었습니다)
비트 필드가 아닌 수명이 겹치는 두 개체는 하나가 다른 개체 내에 중첩되거나 하나 이상의 크기가 0이고 하위 유형 인 경우 동일한 주소를 가질 수 있습니다. 그렇지 않으면 별개의 주소가 있고 분리 된 바이트의 저장 영역을 차지합니다 .
여기서도 “점유”가 정의되어 있지 않다는 문제가 있습니다. 다시 한 번 객체 표현에서 바이트를 사용한다고 주장합니다. 이 [basic.memobj] / 각주 29에 대한 각주가 있습니다.
“as-if”규칙에 따라 구현에서 프로그램이 차이를 관찰 할 수없는 경우 동일한 머신 주소에 두 개의 오브젝트를 저장하거나 오브젝트를 전혀 저장할 수 없습니다 ([intro.execution]).
관찰 가능한 부작용이 없음을 증명할 수 있으면 컴파일러가이를 깨뜨릴 수 있습니다. 나는 이것이 객체 레이아웃과 같은 기본적인 것들에 대해 꽤 복잡하다고 생각합니다. 어쩌면이 최적화가 사용자가 [no_unique_address]
속성 을 추가하여 객체를 분리 할 이유가 없다는 정보를 제공 할 때만 수행되는 이유 일 수 있습니다 .
tl; dr : 패딩은 객체의 일부일 수 있으며 멤버는 분리되어야합니다.
1 나는 점유 할 수있는 참고 문헌을 추가하는 것을 거부 할 수 없었다 : Webster ‘s Revised Unabridged Dictionary, G. & C. Merriam, 1913 (emphasis mine)
- 치수를 유지하거나 채우려면 방이나 공간 을 차지하기 위해; 덮거나 채우는 것; 캠프는 5 에이커의 땅을 차지합니다. J. 허셜 경
사전 크롤링없이 어떤 표준 크롤링이 완료됩니까?