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


12

다음 세 가지를 고려하십시오 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하지 하찮게 복사 가능한 .


나는 그것을 연구하지는 않았지만 "있는 것처럼"규칙을 추측 하고 있습니다. 추상 기계 (코드가 컴파일되는 대상)와 관찰 가능한 차이 (매우 특별한 의미를 가진 용어 btw)가 없으면 컴파일러는 원하는대로 코드를 변경할 수 있습니다.
Jesper Juhl

이것이 이것의 속임수라고 확신하십시오 : stackoverflow.com/questions/53837373/…
NathanOliver

@JesperJuhl-맞습니다. 그러나 왜 그렇게 할 수 없는지 , 왜 그렇게 할 수 없는지 , 그리고 "있는 것처럼"규칙은 전자에 적용되지만 후자는 의미가 없습니다. 또한 "있는 것처럼"은 일반적으로 로컬 레이아웃이 아닌 전역적인 관심사 인 구조 레이아웃에 대해 명확하지 않습니다. 궁극적으로 컴파일러는 "탈출"할 수없는 구조를 제외하고는 레이아웃에 대해 일관된 단일 규칙 세트를 가져야합니다.
BeeOnRope

1
@BeeOnRope 귀하의 질문에 답변을 드릴 수 없습니다. 죄송합니다. 그렇기 때문에 방금 의견이 아닌 의견을 게시했습니다. 당신이 그 의견에서 얻은 것은 설명에 대한 나의 최선의 추측이지만, 나는 대답을 알지 못합니다 (나 자신을 배우는 것이 풍부합니다-이것이 당신이 공감하는 이유입니다).
Jesper Juhl

1
@NicolBolas-올바른 질문에 답하고 있습니까? 안전한 사본이나 다른 것을 감지하는 것이 아닙니다. 오히려 멤버들 사이에서 패딩을 재사용 할 수없는 이유가 궁금합니다. 어쨌든, 당신은 틀 렸습니다 : 사소하게 복사 가능한 것은 유형속성 이며 항상 그렇습니다 . 안전하게 개체를 복사에 그러나, 그것은해야한다 모두 최우수 유형 (유형의 속성을), 그리고 잠재적으로 중첩 피사체 (난 당신이 혼동있어 어디 추측 객체의 속성) 할 수 없습니다. 아직도 우리가 여기서 사본에 대해 이야기하는 이유를 모르겠습니다.
BeeOnRope

답변:


1

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

먼저, 물체의 일부가 무엇인지 알아 봅시다. [기본 유형] / 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. 허셜 경

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


2
into.storage의 "disjoint bytes of storage"부분은 충분하다고 생각합니다. 그러나이 단어는 추가 된 변경의 일부로 C ++ 20에만 추가되었습니다 no_unique_address. C ++ 20 이전의 상황은 덜 명확합니다. basic.life/1.5에서 "객체가 중첩되어 있지 않으면 다른 객체의 공간을 차지할 수 없습니다", 특히 "객체가 차지하는 스토리지가 릴리스 된"방법으로 이어지는 추론을 이해하지 못했습니다. "어떤 물체도 다른 물체의 공간을 차지할 수 없습니다".
BeeOnRope

1
그 단락에 작은 설명을 추가했습니다. 더 이해하기 쉽기를 바랍니다. 그렇지 않으면 내일 다시 볼 것입니다. 지금은 꽤 늦었습니다.
n314159

"두 개의 비트 필드가 하나가 다른 하나에 중첩되는 경우에 동일한 주소가 없거나 수없는 수명을 중첩 개체 적어도 하나의 크기가 0의 하위 객체이고, 이들은 서로 다른 유형의 경우" 중첩 수명의 2 개체 같은 유형, 같은 주소를가 집니다.
언어 변호사

죄송합니다, 좀 더 자세히 설명해 주시겠습니까? 내 대답에서 표준 인용문을 인용하고 그와 약간 충돌하는 예를 제시합니다. 이것이 내 답변에 대한 의견인지, 그것이 대답 해야하는지 여부는 확실하지 않습니다. 귀하의 예와 관련하여 표준의 다른 부분을 고려해야한다고 말하고 싶습니다 (다른 객체에 스토리지를 제공하는 서명되지 않은 char 배열에 대한 단락이 있습니다. 크기가 0 인 기본 최적화와 관련이 있으며 추가로 배치가 새로운 경우 특별 수당, 내가 생각하지 않는 모든 것이 OP와 관련이 있다고 생각합니다)
n314159

@ n314159이 문구에 결함이 있다고 생각합니다.
언어 변호사
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.