[c++] 클래스에서 인접한 멤버가 겹치는 것을 방지하는 것은 무엇입니까?

다음 세 가지를 고려하십시오 struct.

class blub {
    int i;
    char c;

    blub(const blub&) {}
};

class blob {
    char s;

    blob(const blob&) {}
};

struct bla {
    blub b0;
    blob b1;
};

int4 바이트 인 일반 플랫폼에서 크기, 정렬 및 총 패딩 1 은 다음과 같습니다.

  struct   size   alignment   padding
 -------- ------ ----------- ---------
  blub        8           4         3
  blob        1           1         0
  bla        12           4         6

크기 1 이 원칙적으로 패딩에 “적합”할 수 있지만, 부재 blubblob부재 의 보관 사이에는 겹침이 없다 .blobblub

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 할 : 나는 복사 생성자가 정의한 이유입니다 blubblob하지 하찮게 복사 가능한 .



답변

표준은 메모리 모델에 대해 말할 때 매우 조용하고 사용되는 용어 중 일부에 대해서는 명확하지 않습니다. 그러나 나는 일하는 논쟁을 발견했다고 생각합니다 (약간 약할 수 있습니다)

먼저, 물체의 일부가 무엇인지 알아 봅시다. [기본 유형] / 4 :

타입의 오브젝트의 오브젝트 표현 T들의 시퀀스이며 N unsigned char유형의 오브젝트가 차지하는 오브젝트 T, N같음 sizeof(T). type 객체의 값 표현은 type T값을 나타내는 데 참여하는 비트 세트입니다 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)

  1. 치수를 유지하거나 채우려면 방이나 공간 을 차지하기 위해; 덮거나 채우는 것; 캠프는 5 에이커의 땅을 차지합니다. J. 허셜 경

사전 크롤링없이 어떤 표준 크롤링이 완료됩니까?


답변