Programing

집계 및 POD 란 무엇이며 어떻게 / 왜 특별합니까?

lottogame 2020. 2. 11. 22:21
반응형

집계 및 POD 란 무엇이며 어떻게 / 왜 특별합니까?


FAQ 는 집계 및 POD에 관한 것으로 다음 자료를 다룹니다.

  • 집계 란 무엇입니까 ?
  • 무엇 POD 의 (일반 오래된 데이터)?
  • 그들은 어떻게 관련되어 있습니까?
  • 어떻게 그리고 왜 특별합니까?
  • C ++ 11의 변경 사항은 무엇입니까?

읽는 방법:

이 기사는 다소 길다. 집계와 POD (Plain Old Data)에 대해 알고 싶다면 시간이 걸리고 읽어보십시오. 집계에만 관심이 있다면 첫 번째 부분 만 읽으십시오. 당신은 단지 포드에 관심이 있다면 당신은 첫째 집계의 정의, 의미 및 예제를 읽을 수 있어야합니다 그리고 당신은 할 수 있습니다 포드로 이동하지만, 난 여전히 전체의 첫 부분을 읽어 보시기 바랍니다 것입니다. 집계 개념은 POD를 정의하는 데 필수적입니다. 문법, 스타일, 서식, 구문 등 사소한 오류라도 발견되면 의견을 남겨주세요. 편집하겠습니다.

이 답변은 C ++ 03에 적용됩니다. 다른 C ++ 표준은 다음을 참조하십시오.

집계 란 무엇이고 왜 특별한가

C ++ 표준 ( C ++ 03 8.5.1 §1 )의 공식 정의 :

집합은 사용자 선언 생성자 (12.1), 비공개 또는 보호 된 비 정적 데이터 멤버 (절 11), 기본 클래스 (절 10) 및 가상 함수 (10.3)가없는 배열 또는 클래스 (9 절)입니다. ).

자,이 정의를 해석해 봅시다. 우선, 모든 배열은 집계입니다. 다음과 같은 경우 클래스도 집합체가 될 수 있습니다. 구조체 또는 공용체에 대해 아무 것도 말하지 않습니다. 집계 할 수 없습니까? 네, 그들은 할 수 있어요. C ++ class에서이 용어 는 모든 클래스, 구조체 및 공용체를 나타냅니다. 따라서 클래스 (또는 구조체 또는 공용체)는 위 정의의 기준을 충족하는 경우에만 집계입니다. 이러한 기준은 무엇을 의미합니까?

  • 이것은 집계 클래스가 생성자를 가질 수 없다는 것을 의미하지는 않습니다. 실제로 기본 생성자 및 / 또는 복사 생성자가 컴파일러에 의해 암시 적으로 선언되고 사용자가 명시 적으로 선언하지 않는 한 생성자가있을 수 있습니다

  • 비공개 또는 보호 된 비 정적 데이터 멤버가 없습니다 . 원하는만큼 개인 및 보호 멤버 함수 (생성자는 아님)뿐만 아니라 개인 또는 보호 된 정적 데이터 멤버 및 멤버 함수를 가질 수 있으며 집계 클래스 규칙을 위반하지 않을 수 있습니다

  • 집계 클래스에는 사용자 선언 / 사용자 정의 사본 할당 연산자 및 / 또는 소멸자가있을 수 있습니다.

  • 배열은 집계되지 않은 클래스 유형의 배열 인 경우에도 집계입니다.

이제 몇 가지 예를 살펴 보겠습니다.

class NotAggregate1
{
  virtual void f() {} //remember? no virtual functions
};

class NotAggregate2
{
  int x; //x is private by default and non-static 
};

class NotAggregate3
{
public:
  NotAggregate3(int) {} //oops, user-defined constructor
};

class Aggregate1
{
public:
  NotAggregate1 member1;   //ok, public member
  Aggregate1& operator=(Aggregate1 const & rhs) {/* */} //ok, copy-assignment  
private:
  void f() {} // ok, just a private function
};

당신은 아이디어를 얻습니다. 이제 집계가 어떻게 특별한 지 봅시다. 집계되지 않은 클래스와 달리 중괄호로 초기화 할 수 있습니다 {}. 이 초기화 구문은 일반적으로 배열로 알려져 있으며 우리는 이것이 집합이라는 것을 알게되었습니다. 이제부터 시작하겠습니다.

Type array_name[n] = {a1, a2, …, am};

(m == N)의 경우,
제 i 번째 배열 요소가 초기화되는 I
다른 경우 (m <n)의
어레이의 제 m 소자는으로 초기화되는 하나 하는 2 ...하는 m 다른n - m요소 가능한 경우이다 값을 초기화 (용어의 설명은 아래 참조)
그 밖의 경우 (m> n)의
에러 발급 컴파일러
다른을 (이러한 경우 n이 추천 전혀 지정되지 않은 int a[] = {1, 2, 3};)
의 크기를 어레이 (N) 따라서, m 동일한 것으로 가정int a[] = {1, 2, 3};동등int a[3] = {1, 2, 3};

스칼라 타입 (객체의 경우 bool, int, char, double, 포인터 등) 인 값으로 초기화 는이 초기화되는 수단 0해당 유형 ( falsebool, 0.0double등). 사용자 선언 기본 생성자가있는 클래스 유형의 개체가 값으로 초기화되면 기본 생성자가 호출됩니다. 기본 생성자가 내재적으로 정의 된 경우 모든 비 정적 멤버는 재귀 적으로 값으로 초기화됩니다. 이 정의는 부정확하고 약간 부정확하지만 기본 아이디어를 제공해야합니다. 참조는 값을 초기화 할 수 없습니다. 예를 들어 클래스에 적절한 기본 생성자가없는 경우 집계되지 않은 클래스의 값 초기화에 실패 할 수 있습니다.

배열 초기화의 예 :

class A
{
public:
  A(int) {} //no default constructor
};
class B
{
public:
  B() {} //default constructor available
};
int main()
{
  A a1[3] = {A(2), A(1), A(14)}; //OK n == m
  A a2[3] = {A(2)}; //ERROR A has no default constructor. Unable to value-initialize a2[1] and a2[2]
  B b1[3] = {B()}; //OK b1[1] and b1[2] are value initialized, in this case with the default-ctor
  int Array1[1000] = {0}; //All elements are initialized with 0;
  int Array2[1000] = {1}; //Attention: only the first element is 1, the rest are 0;
  bool Array3[1000] = {}; //the braces can be empty too. All elements initialized with false
  int Array4[1000]; //no initializer. This is different from an empty {} initializer in that
  //the elements in this case are not value-initialized, but have indeterminate values 
  //(unless, of course, Array4 is a global array)
  int array[2] = {1, 2, 3, 4}; //ERROR, too many initializers
}

이제 중괄호로 집계 클래스를 초기화하는 방법을 살펴 보겠습니다. 거의 같은 방식입니다. 배열 요소 대신 정적이 아닌 데이터 멤버를 클래스 정의에 나타나는 순서대로 초기화합니다 (모두 정의에 의해 모두 공개됨). 멤버보다 이니셜 라이저가 적은 경우 나머지는 값으로 초기화됩니다. 명시 적으로 초기화되지 않은 멤버 중 하나의 값을 초기화 할 수 없으면 컴파일 타임 오류가 발생합니다. 필요한 것보다 많은 이니셜 라이저가 있으면 컴파일 타임 오류도 발생합니다.

struct X
{
  int i1;
  int i2;
};
struct Y
{
  char c;
  X x;
  int i[2];
  float f; 
protected:
  static double d;
private:
  void g(){}      
}; 

Y y = {'a', {10, 20}, {20, 30}};

위의 예 y.c와 함께 초기화된다 'a', y.x.i110, y.x.i220, y.i[0]20, y.i[1]30y.f가치 초기화로 초기화된다 0.0. 보호 된 정적 멤버 d는이므로 초기화되지 않습니다 static.

집계 공용체는 첫 번째 멤버 만 중괄호로 초기화 할 수 있다는 점에서 다릅니다. C ++에서 노조 사용을 고려할 정도로 충분히 진보 된 경우 (사용이 매우 위험 할 수 있으며 신중하게 고려해야 함) 표준에서 노조 규칙을 직접 찾을 수 있다고 생각합니다. :).

이제 집계에 대한 특별한 점을 알았으므로 클래스의 제한 사항을 이해해 봅시다. 즉, 왜 거기에 있습니다. 중괄호를 사용하여 멤버 단위로 초기화하면 클래스가 멤버의 합계에 지나지 않는다는 것을 의미합니다. 사용자 정의 생성자가있는 경우 사용자가 멤버를 초기화하기 위해 추가 작업을 수행해야하므로 중괄호 초기화가 올바르지 않습니다. 가상 함수가 존재한다면,이 클래스의 객체는 (대부분의 구현에서) 생성자에 설정된 클래스의 vtable에 대한 포인터를 가지므로 괄호 초기화가 불충분합니다. 연습과 유사한 방식으로 나머지 제한을 알아낼 수 있습니다 :).

집계에 대해서는 충분합니다. 이제 더 엄격한 유형의 집합을 정의 할 수 있습니다.

POD 란 무엇이며 왜 특별합니까?

C ++ 표준 ( C ++ 03 9 §4 )의 공식 정의 :

POD-struct는 non-POD-struct, non-POD-union (또는 이러한 유형의 배열) 또는 참조 유형의 비 정적 데이터 멤버가없고 사용자 정의 사본 할당 연산자가없고, 사용자 정의 소멸자. 마찬가지로, POD 연합은 non-POD-struct 유형, non-POD-union (또는 이러한 유형의 배열) 또는 참조의 비 정적 데이터 멤버가없고 사용자 정의 사본 할당 연산자가없는 집계 공용체입니다. 그리고 사용자 정의 소멸자가 없습니다. POD 클래스는 POD 구조체 또는 POD 연합 인 클래스입니다.

와우, 파싱하기가 더 힘들지 않나요? :) 위와 같은 이유로 노조를 제외하고 좀 더 명확한 방식으로 문구를 바꿔 보자.

집계 정의 클래스는 사용자 정의 사본 할당 연산자 및 소멸자가없고 비 정적 구성원이 비 POD 클래스, 비 POD 배열 또는 참조가 아닌 경우 POD라고합니다.

이 정의는 무엇을 의미합니까? ( PODPlain Old Data를 의미 한다고 언급 했습니까 ?)

  • 모든 POD 클래스는 집계이거나 클래스를 집계하지 않으면 POD가 아닌 것입니다.
  • 표준 용어가 두 경우 모두 POD 구조이지만 구조체와 마찬가지로 클래스는 POD가 될 수 있습니다.
  • 집계의 경우와 마찬가지로 클래스에 어떤 정적 멤버가 있는지는 중요하지 않습니다.

예 :

struct POD
{
  int x;
  char y;
  void f() {} //no harm if there's a function
  static std::vector<char> v; //static members do not matter
};

struct AggregateButNotPOD1
{
  int x;
  ~AggregateButNotPOD1() {} //user-defined destructor
};

struct AggregateButNotPOD2
{
  AggregateButNotPOD1 arrOfNonPod[3]; //array of non-POD class
};

POD 클래스, POD 연합, 스칼라 유형 및 이러한 유형의 배열을 통칭하여 POD 유형 이라고 합니다.
POD는 여러 가지면에서 특별합니다. 몇 가지 예만 제공하겠습니다.

  • POD 클래스는 C 구조체에 가장 가깝습니다. POD와 달리 POD는 멤버 함수와 임의의 정적 멤버를 가질 수 있지만이 두 가지 중 어느 것도 객체의 메모리 레이아웃을 변경하지 않습니다. 따라서 C 및 .NET에서 사용할 수있는 이식성이 뛰어난 동적 라이브러리를 작성하려면 내 보낸 모든 함수가 POD 유형의 매개 변수 만 가져오고 리턴하도록해야합니다.

  • 비 POD 클래스 유형의 객체 수명은 생성자가 완료되면 시작되고 소멸자가 완료되면 종료됩니다. POD 클래스의 경우 수명은 오브젝트의 스토리지가 점유 될 때 시작되고 해당 스토리지가 해제되거나 재사용 될 때 완료됩니다.

  • POD 유형의 객체의 경우 표준 memcpy에 따라 객체의 내용을 char 또는 unsigned char 배열에 넣은 다음 memcpy내용을 다시 객체에 넣을 때 객체의 원래 값을 유지합니다. 비 POD 유형의 객체에 대해서는 그러한 보증이 없습니다. 또한을 사용하여 POD 객체를 안전하게 복사 할 수 있습니다 memcpy. 다음 예제에서는 T가 POD 유형이라고 가정합니다.

    #define N sizeof(T)
    char buf[N];
    T obj; // obj initialized to its original value
    memcpy(buf, &obj, N); // between these two calls to memcpy,
    // obj might be modified
    memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type
    // holds its original value
    
  • 고토 진술. 아시다시피, 일부 변수가 아직 범위에 있지 않은 지점에서 이미 범위에있는 지점으로 이동하여 이동하는 것은 불법입니다 (컴파일러가 오류를 발행해야 함). 이 제한 사항은 변수가 비 POD 유형 인 경우에만 적용됩니다. 다음 예에서는 형식 f()이 잘못되었지만 형식 g()잘못 되었습니다. Microsoft의 컴파일러는이 규칙에 너무 자유롭기 때문에 두 경우 모두 경고를 표시합니다.

    int f()
    {
      struct NonPOD {NonPOD() {}};
      goto label;
      NonPOD x;
    label:
      return 0;
    }
    
    int g()
    {
      struct POD {int i; char c;};
      goto label;
      POD x;
    label:
      return 0;
    }
    
  • POD 객체의 시작 부분에 패딩이없는 것이 보장됩니다. 포드-클래스 A의 첫 번째 멤버는 타입 T의 경우 즉, 안전하게 할 수 reinterpret_cast에서 A*까지 T*포인터를 얻을 반대로 제 1 부재와 그 반대합니다.

목록은 계속됩니다…

결론

아시다시피, 많은 언어 기능이 다르게 작동하기 때문에 POD가 무엇인지 정확히 이해하는 것이 중요합니다.


C ++ 11의 변경 사항은 무엇입니까?

집계

집계의 표준 정의는 약간 변경되었지만 여전히 거의 동일합니다.

집계는 사용자 제공 생성자 (12.1)가없고 비 정적 데이터 멤버 (9.2)에 대한 중괄호 또는 이니셜 라이저 가없고 개인용 또는 보호 된 비 정적 데이터 멤버 가없는 배열 또는 클래스 (Clause 9)입니다 ( 조항 11), 기본 등급 없음 (10 항) 및 가상 기능 없음 (10.3).

좋아, 무엇이 바뀌 었습니까?

  1. 이전에는 집계에 사용자가 선언 한 생성자가 없었지만 이제는 사용자가 제공 한 생성자 가있을 수 없습니다 . 차이가 있습니까? 그렇습니다. 생성자를 선언하고 기본값을 지정할 수 있기 때문입니다 .

    struct Aggregate {
        Aggregate() = default; // asks the compiler to generate the default implementation
    };
    

    첫 번째 선언에서 기본값으로 지정된 생성자 (또는 특수 멤버 함수) 사용자가 제공하지 않기 때문에 여전히 집계 입니다.

  2. 이제 집계 에 비 정적 데이터 멤버에 대한 중괄호 또는 이니셜 라이저사용할 수 없습니다 . 이것은 무엇을 의미 하는가? 이 새로운 표준을 사용하면 다음과 같이 클래스에서 직접 멤버를 초기화 할 수 있습니다.

    struct NotAggregate {
        int x = 5; // valid in C++11
        std::vector<int> s{1,2,3}; // also valid
    };
    

    이 기능을 사용하면 기본적으로 고유 한 기본 생성자를 제공하는 것과 동일하므로 클래스가 더 이상 집계되지 않습니다.

따라서 집합체는 전혀 변하지 않았습니다. 새로운 기능에 맞게 여전히 동일한 기본 아이디어입니다.

포드는 어떻습니까?

POD는 많은 변화를 겪었습니다. 이 새로운 표준에서 POD에 대한 많은 이전 규칙이 완화되었으며 표준에서 정의가 제공되는 방식이 크게 변경되었습니다.

POD의 아이디어는 기본적으로 두 가지 고유 한 속성을 캡처하는 것입니다.

  1. 정적 초기화를 지원하며
  2. C ++에서 POD를 컴파일하면 C로 컴파일 된 구조체와 동일한 메모리 레이아웃이 제공됩니다.

이 때문에 정의는 사소한 클래스와 표준 레이아웃 클래스 라는 두 가지 고유 한 개념으로 나뉩니다 . POD보다 유용하기 때문입니다. 이 표준은 이제 거의 더 구체적인 선호하는 용어는 POD를 사용하지 사소한표준 레이아웃 개념을.

새로운 정의는 기본적으로 POD가 사소하고 표준 레이아웃을 갖는 클래스이며,이 속성은 모든 비 정적 데이터 멤버에 대해 재귀 적으로 보유해야한다고 말합니다.

POD 구조체는 간단한 클래스와 표준 레이아웃 클래스 인 비 유니언 클래스이며 비 POD 구조체 형식, 비 POD 공용체 (또는 이러한 형식의 배열)의 비 정적 데이터 멤버가 없습니다. 마찬가지로 POD 공용체는 사소한 클래스 및 표준 레이아웃 클래스 인 공용체이며 비 -POD 구조체 형식, 비 -POD 공용체 (또는 이러한 형식의 배열)의 비 정적 데이터 멤버가 없습니다. POD 클래스는 POD 구조체 또는 POD 공용체 인 클래스입니다.

이 두 속성 각각에 대해 자세히 살펴 보겠습니다.

사소한 수업

간이는 사소한 클래스는 정적 초기화를 지원 제 재산권 상기 언급된다. 클래스가 사소하게 복사 가능한 경우 (사소한 클래스의 수퍼 세트), 장소와 같은 memcpy것으로 표현을 복사하고 결과가 동일 할 것으로 기대할 수 있습니다.

표준은 다음과 같이 간단한 클래스를 정의합니다.

사소하게 복사 가능한 클래스는 다음과 같은 클래스입니다.

— 사소한 사본 생성자가 없습니다 (12.8).

— 사소한 이동 생성자가 없습니다 (12.8).

— 사소한 사본 할당 연산자가 없습니다 (13.5.3, 12.8).

— 사소한 이동 할당 연산자 (13.5.3, 12.8)가 없으며

— 사소한 소멸자가 있습니다 (12.4).

사소한 클래스는 사소한 기본 생성자 (12.1)를 가지고 있으며 사소하게 복사 할 수있는 클래스입니다.

[ 참고 : 특히 사소한 복사 가능 또는 사소한 클래스에는 가상 기능 또는 가상 기본 클래스가 없습니다. — 끝 참고 ]

그렇다면 사소하고 사소한 것은 무엇입니까?

클래스 X에 대한 복사 / 이동 생성자는 사용자가 제공하지 않은 경우 사소합니다.

— 클래스 X에는 가상 기능 (10.3) 및 가상 기본 클래스 (10.1)가 없으며

— 각 직접 기본 클래스 하위 오브젝트를 복사 / 이동하도록 선택된 생성자는 사소한 것이며

-클래스 타입 (또는 그 배열) 인 X의 각각의 비 정적 데이터 멤버에 대해, 그 멤버를 복사 / 이동하도록 선택된 생성자는 사소하다.

그렇지 않으면 복사 / 이동 생성자가 중요하지 않습니다.

기본적으로 이것은 복사 또는 이동 생성자가 사용자가 제공하지 않고 클래스에 가상이 없으며 클래스의 모든 멤버와 기본 클래스에 대해 재귀 적으로 보유하는 경우 사소한 것을 의미합니다.

간단한 복사 / 이동 할당 연산자의 정의는 "constructor"라는 단어를 "assignment operator"로 바꾸는 것과 매우 유사합니다.

사소한 소멸자도 비슷한 정의를 가지고 있으며 가상이 될 수 없다는 제약이 추가되었습니다.

그리고 또 다른 유사한 규칙이 사소한 기본 생성자에도 존재하며, 클래스에 brace-or-equal-initializers 가있는 비 정적 데이터 멤버가있는 경우 기본 생성자가 사소한 것이 아니라는 점이 있습니다.

다음은 모든 것을 정리하는 몇 가지 예입니다.

// empty classes are trivial
struct Trivial1 {};

// all special members are implicit
struct Trivial2 {
    int x;
};

struct Trivial3 : Trivial2 { // base class is trivial
    Trivial3() = default; // not a user-provided ctor
    int y;
};

struct Trivial4 {
public:
    int a;
private: // no restrictions on access modifiers
    int b;
};

struct Trivial5 {
    Trivial1 a;
    Trivial2 b;
    Trivial3 c;
    Trivial4 d;
};

struct Trivial6 {
    Trivial2 a[23];
};

struct Trivial7 {
    Trivial6 c;
    void f(); // it's okay to have non-virtual functions
};

struct Trivial8 {
     int x;
     static NonTrivial1 y; // no restrictions on static members
};

struct Trivial9 {
     Trivial9() = default; // not user-provided
      // a regular constructor is okay because we still have default ctor
     Trivial9(int x) : x(x) {};
     int x;
};

struct NonTrivial1 : Trivial3 {
    virtual void f(); // virtual members make non-trivial ctors
};

struct NonTrivial2 {
    NonTrivial2() : z(42) {} // user-provided ctor
    int z;
};

struct NonTrivial3 {
    NonTrivial3(); // user-provided ctor
    int w;
};
NonTrivial3::NonTrivial3() = default; // defaulted but not on first declaration
                                      // still counts as user-provided
struct NonTrivial5 {
    virtual ~NonTrivial5(); // virtual destructors are not trivial
};

표준 레이아웃

표준 레이아웃 은 두 번째 속성입니다. 표준은 이것들이 다른 언어와의 통신에 유용하다고 언급하며, 표준 레이아웃 클래스는 동등한 C 구조체 또는 공용체의 동일한 메모리 레이아웃을 갖기 때문입니다.

멤버와 모든 기본 클래스에 대해 재귀 적으로 보유해야하는 또 다른 속성입니다. 그리고 평소와 같이 가상 함수 나 가상 기본 클래스는 허용되지 않습니다. 그러면 레이아웃이 C와 호환되지 않습니다.

여기서 완화 된 규칙은 표준 레이아웃 클래스에는 동일한 액세스 제어를 가진 모든 비 정적 데이터 멤버가 있어야한다는 것입니다. 이전에는 모두 공개 되어야 했지만 이제는 모두 비공개이거나 모두 보호되는 한 비공개 또는 보호 할 수 있습니다.

상속을 사용 하는 경우 전체 상속 트리에서 하나의 클래스 에만 정적이 아닌 데이터 멤버가있을 수 있으며 첫 번째 비 정적 데이터 멤버는 기본 클래스 유형일 수 없습니다 (이는 앨리어싱 규칙을 위반할 수 있음). 그렇지 않으면 표준이 아닙니다. 레이아웃 클래스.

이것이 표준 텍스트에서 정의가 이루어지는 방식입니다.

표준 레이아웃 클래스는 다음과 같은 클래스입니다.

— 비표준 레이아웃 클래스 유형 (또는 이러한 유형의 배열) 또는 참조의 비 정적 데이터 멤버가 없습니다.

— 가상 기능 (10.3) 및 가상 기본 클래스 (10.1)가 없습니다.

— 모든 비 정적 데이터 멤버에 대해 동일한 액세스 제어 (Clause 11)가 있습니다.

— 비표준 레이아웃 기본 클래스가 없습니다.

— 가장 파생 된 클래스에 비 정적 데이터 멤버가없고 비 정적 데이터 멤버가있는 기본 클래스가 하나만 있거나 비 정적 데이터 멤버가있는 기본 클래스가없고

— 첫 번째 비 정적 데이터 멤버와 동일한 유형의 기본 클래스가 없습니다.

표준 레이아웃 구조체는 클래스 키 구조체 또는 클래스 키 클래스로 정의 된 표준 레이아웃 클래스입니다.

표준 레이아웃 공용체는 클래스 키 공용체로 정의 된 표준 레이아웃 클래스입니다.

[ 참고 : 표준 레이아웃 클래스는 다른 프로그래밍 언어로 작성된 코드와 통신하는 데 유용합니다. 그들의 레이아웃은 9.2에 명시되어 있습니다. — 끝 참고 ]

그리고 몇 가지 예를 봅시다.

// empty classes have standard-layout
struct StandardLayout1 {};

struct StandardLayout2 {
    int x;
};

struct StandardLayout3 {
private: // both are private, so it's ok
    int x;
    int y;
};

struct StandardLayout4 : StandardLayout1 {
    int x;
    int y;

    void f(); // perfectly fine to have non-virtual functions
};

struct StandardLayout5 : StandardLayout1 {
    int x;
    StandardLayout1 y; // can have members of base type if they're not the first
};

struct StandardLayout6 : StandardLayout1, StandardLayout5 {
    // can use multiple inheritance as long only
    // one class in the hierarchy has non-static data members
};

struct StandardLayout7 {
    int x;
    int y;
    StandardLayout7(int x, int y) : x(x), y(y) {} // user-provided ctors are ok
};

struct StandardLayout8 {
public:
    StandardLayout8(int x) : x(x) {} // user-provided ctors are ok
// ok to have non-static data members and other members with different access
private:
    int x;
};

struct StandardLayout9 {
    int x;
    static NonStandardLayout1 y; // no restrictions on static members
};

struct NonStandardLayout1 {
    virtual f(); // cannot have virtual functions
};

struct NonStandardLayout2 {
    NonStandardLayout1 X; // has non-standard-layout member
};

struct NonStandardLayout3 : StandardLayout1 {
    StandardLayout1 x; // first member cannot be of the same type as base
};

struct NonStandardLayout4 : StandardLayout3 {
    int z; // more than one class has non-static data members
};

struct NonStandardLayout5 : NonStandardLayout3 {}; // has a non-standard-layout base class

결론

이러한 새로운 규칙을 통해 훨씬 더 많은 유형이 POD가 될 수 있습니다. 유형이 POD가 아니더라도 일부 POD 속성을 개별적으로 활용할 수 있습니다 (사소하거나 표준 레이아웃 중 하나 일 경우).

표준 라이브러리에는 헤더에서 이러한 속성을 테스트하는 특성이 있습니다 <type_traits>.

template <typename T>
struct std::is_pod;
template <typename T>
struct std::is_trivial;
template <typename T>
struct std::is_trivially_copyable;
template <typename T>
struct std::is_standard_layout;

C ++ 14에서 변경된 사항

참조를 위해 Draft C ++ 14 표준 을 참조 할 수 있습니다.

집계

이것은 다음과 같은 정의를 제공하는 8.5.1 집계 섹션에서 다룹니다 .

집계는 사용자 제공 생성자 (12.1), 개인 또는 보호 된 비 정적 데이터 멤버 (Clause 11), 기본 클래스 (Clause 10) 및 가상 함수 (10.3)가없는 배열 또는 클래스 (Clause 9)입니다. ).

유일한 변경 사항은 이제 클래스 내 멤버 이니셜 라이저를 추가 해도 클래스가 집계되지 않습니다. 따라서 C ++ 11 의 다음 예제는 멤버 인 이니셜 라이저가있는 클래스에 대한 집계 초기화입니다 .

struct A
{
  int a = 3;
  int b = 3;
};

C ++ 11에서는 집계가 아니지만 C ++ 14에 있습니다. 이 변경 사항은 N3605 : 멤버 이니셜 라이저 및 집계 에서 다루며 다음과 같은 요약을 갖습니다.

Bjarne Stroustrup과 Richard Smith는 집계 초기화와 멤버 이니셜 라이저가 함께 작동하지 않는 문제를 제기했습니다. 이 백서에서는 Smith가 제안한 단어를 채택하여 집계에서 멤버 초기화 도구를 사용할 수없는 제한을 제거함으로써 문제를 해결하도록 제안합니다.

포드는 그대로 유지

POD ( plain old data ) 구조체에 대한 정의는 9 클래스 섹션에서 다루고 있습니다 .

POD 구조체 ( 110) 는 사소한 클래스 및 표준 레이아웃 클래스 인 비 유니언 클래스이며 비 -POD 구조체 유형, 비 -POD 유니언 (또는 이러한 유형의 어레이)의 비 정적 데이터 멤버를 갖지 않는다. 마찬가지로 POD 공용체는 사소한 클래스 및 표준 레이아웃 클래스 인 공용체이며 비 POD 구조체 형식, 비 POD 공용체 (또는 이러한 형식의 배열)의 비 정적 데이터 멤버가 없습니다. POD 클래스는 POD 구조체 또는 POD 공용체 인 클래스입니다.

이것은 C ++ 11과 같은 표현입니다.

C ++ 14의 표준 레이아웃 변경

주석에서 언급했듯이 포드표준 레이아웃 의 정의에 의존 하며 C ++ 14의 변경 사항은 사실이지만 C ++ 14에 적용된 결함 보고서를 통해 이루어졌습니다.

3 개의 DR이있었습니다 :

따라서 표준 레이아웃 은이 Pre C ++ 14에서 나왔습니다.

표준 레이아웃 클래스는 다음과 같은 클래스입니다.

  • (7.1) 비표준 레이아웃 클래스 유형 (또는 이러한 유형의 배열) 또는 참조의 비 정적 데이터 멤버가 없습니다.
  • (7.2)에는 가상 함수 ([class.virtual])와 가상 기본 클래스 ([class.mi])가 없습니다.
  • (7.3) 모든 비 정적 데이터 멤버에 대해 동일한 액세스 제어 (Clause [class.access])를 갖습니다.
  • (7.4)에는 비표준 레이아웃 기본 클래스가 없습니다.
  • (7.5) 가장 파생 된 클래스에는 비 정적 데이터 멤버가없고 비 정적 데이터 멤버가있는 기본 클래스가 하나만 있거나 비 정적 데이터 멤버가있는 기본 클래스가없고
  • (7.6)에는 첫 번째 비 정적 데이터 멤버와 동일한 유형의 기본 클래스가 없습니다 .109

C ++ 14이 :

클래스 S는 다음과 같은 경우 표준 레이아웃 클래스입니다.

  • (3.1) 비표준 레이아웃 클래스 유형 (또는 이러한 유형의 배열) 또는 참조의 비 정적 데이터 멤버가 없습니다.
  • (3.2) 가상 기능과 가상 기본 클래스가 없으며,
  • (3.3) 모든 비 정적 데이터 멤버에 대해 동일한 액세스 제어를 갖습니다.
  • (3.4) 비표준 레이아웃 기본 클래스가 없습니다.
  • (3.5) 주어진 유형의 기본 클래스 하위 객체를 하나 이상
  • (3.6) 클래스와 그 기본 클래스의 모든 비 정적 데이터 멤버와 비트 필드가 동일한 클래스에서 처음 선언되었으며,
  • (3.7)은 기본 클래스로서 유형의 세트 M (S)의 요소를 갖지 않으며, 임의의 유형 X에 대해 M (X)는 다음과 같이 정의된다. 104 [주 : M (X)는 유형의 세트이다 X에서 0 오프셋에있을 수있는 모든 비 기본 클래스 하위 오브젝트 — 종료 참고]
    • (3.7.1) X가 비 정적 데이터 멤버가없는 (비상 속적으로 상속 된) 비 유니언 클래스 유형 인 경우, 세트 M (X)는 비어 있습니다.
    • (3.7.2) X가 크기가 0이거나 X의 첫 번째 비 정적 데이터 멤버 인 X0 유형의 비 정적 데이터 멤버가있는 비 유니언 클래스 유형 인 경우 (이 멤버는 익명 공용체 일 수 있음) )에서 세트 M (X)는 X0과 M (X0)의 요소로 구성됩니다.
    • (3.7.3) X가 공용체 유형 인 경우, 집합 M (X)는 모든 M (Ui)과 모든 Ui를 포함하는 집합의 합집합입니다. 여기서 각 Ui는 X의 i 번째 비 정적 데이터 멤버의 형식입니다. .
    • (3.7.4) X가 요소 유형이 Xe 인 배열 유형 인 경우 세트 M (X)는 Xe와 M (Xe)의 요소로 구성됩니다.
    • (3.7.5) X가 클래스가 아닌 비 배열 유형 인 경우 집합 M (X)은 비어 있습니다.

다음 규칙을 자세히 설명해 주시겠습니까?

나는 시도 할 것이다 :

a) 표준 레이아웃 클래스에는 동일한 액세스 제어를 가진 모든 비 정적 데이터 멤버가 있어야합니다.

그건 간단합니다 : 모든 비 정적 데이터 멤버가 있어야 모든public, private또는 protected. 당신은 일부 public와 일부를 가질 수 없습니다 private.

그것들에 대한 추론은 "표준 레이아웃"과 "표준 레이아웃이 아님"을 전혀 구분하지 않는 이유입니다. 즉, 컴파일러에게 물건을 메모리에 넣는 방법을 자유롭게 선택할 수 있도록합니다. vtable 포인터에 관한 것이 아닙니다.

98 년에 C ++을 표준화했을 때 사람들은 기본적으로 사람들이 C ++를 어떻게 구현할 것인지 예측해야했습니다. 그들은 다양한 C ++의 풍미에 대해 약간의 구현 경험을 가지고 있었지만, 일에 대해 확신하지 못했습니다. 그래서 그들은 신중을 기하기로 결정했다. 컴파일러에게 가능한 많은 자유를 주라.

이것이 C ++ 98에서 POD의 정의가 너무 엄격한 이유입니다. C ++ 컴파일러는 대부분의 클래스에서 멤버 레이아웃에 대해 위도를 부여했습니다. 기본적으로 POD 유형은 특별한 경우를 위해 고안된 것으로 사용자가 구체적으로 작성한 이유입니다.

C ++ 11을 작업 할 때는 컴파일러에 대한 경험이 훨씬 많았습니다. 그리고 그들은 깨달았습니다 ... C ++ 컴파일러 작성자는 정말 게으르다. 그들은이 모든 자유를했다,하지만 그들은하지 않았다 어떻게 그것으로 아무것도.

표준 레이아웃의 규칙은 일반적인 관행을 다소 체계화합니다. 대부분의 컴파일러는 구현하기 위해 (해당 유형 특성의 일부가 아닌 경우) 실제로 많은 것을 변경할 필요가 없었습니다.

이제 public/에 관해서는 private상황이 다릅니다. 어떤 멤버가 publicvs. private실제로 순서를 바꿀 수있는 자유는 특히 디버깅 빌드에서 컴파일러에게 중요 할 수 있습니다. 표준 레이아웃의 요점은 다른 언어와의 호환성이 있다는 것이므로 디버그와 릴리스에서 레이아웃을 다르게 할 수는 없습니다.

그러면 실제로 사용자를 해치지 않는다는 사실이 있습니다. 캡슐화 된 클래스를 만드는 경우 모든 데이터 멤버가 private어쨌든 될 가능성이 높습니다. 일반적으로 공개 데이터 멤버를 완전히 캡슐화 된 유형에 노출시키지 않습니다. 따라서 이는 해당 부서를 원하는 소수의 사용자에게만 문제가됩니다.

따라서 큰 손실이 없습니다.

b) 전체 상속 트리에서 오직 하나의 클래스 만이 비 정적 데이터 멤버를 가질 수있다.

이것의 이유는 그들이 표준 레이아웃을 다시 표준화 한 이유, 즉 일반적인 관행으로 되돌아옵니다.

실제로 물건을 저장하는 상속 트리의 두 멤버를 갖는 경우 에는 일반적인 관행 없습니다 . 일부는 파생 클래스보다 기본 클래스를 배치하고 다른 클래스는 다른 방식으로 수행합니다. 두 개의 기본 클래스에서 온 멤버를 어떤 방법으로 주문합니까? 등등. 컴파일러는 이러한 질문에 대해 크게 의견을 나누고 있습니다.

또한 0 / 1 / 무한 규칙으로 인해 멤버와 함께 두 개의 클래스를 가질 수 있다고 말하면 원하는만큼 말할 수 있습니다. 이를 처리하는 방법에 대한 많은 레이아웃 규칙을 추가해야합니다. 다중 상속이 어떻게 작동하는지, 어떤 클래스가 다른 클래스보다 먼저 데이터를 넣는 등의 방법을 말해야합니다. 이는 물질적 이득이 거의없는 규칙입니다.

가상 함수 및 기본 생성자 표준 레이아웃이없는 모든 것을 만들 수는 없습니다.

정적이 아닌 첫 번째 데이터 멤버는 기본 클래스 유형일 수 없습니다 (이는 앨리어싱 규칙을 위반할 수 있음).

나는 이것에 대해 실제로 말할 수 없다. 나는 C ++의 앨리어싱 규칙을 실제로 이해하기에 충분한 교육을받지 못했습니다. 그러나 기본 멤버가 기본 클래스 자체와 동일한 주소를 공유한다는 사실과 관련이 있습니다. 그건:

struct Base {};
struct Derived : Base { Base b; };

Derived d;
static_cast<Base*>(&d) == &d.b;

그리고 이것은 아마도 C ++의 앨리어싱 규칙에 위배됩니다. 어떤 식 으로든

그러나 이것을 고려하십시오 : 실제로 이것을 수행하는 능력이 얼마나 유용 할 수 있습니까? 비 정적 데이터 멤버를 가질 수있는 클래스는 하나뿐이므로 해당 멤버 Derived여야합니다 ( 멤버가 멤버이므로 Base). 따라서 데이터가 비어 Base 있어야합니다 . 그리고 기본 클래스 뿐만 아니라Base 비어있는 경우 ... 왜 데이터 멤버가 있습니까?

Base비어 있기 때문에 상태가 없습니다. 따라서 정적이 아닌 멤버 함수는 this포인터가 아닌 매개 변수를 기반으로 수행하는 작업을 수행 합니다.

다시 한번 : 큰 손실은 없습니다.


C ++ 17의 변화

여기 에서 C ++ 17 국제 표준 최종 초안을 다운로드 하십시오 .

집계

C ++ 17은 집계 및 집계 초기화를 확장하고 향상시킵니다. 표준 라이브러리에는 이제 std::is_aggregate유형 특성 클래스 도 포함됩니다 . 섹션 11.6.1.1 및 11.6.1.2의 공식 정의는 다음과 같습니다 (내부 참조 제거).

집계는
사용자 제공, 명시 적 또는 상속 된 생성자,
개인 또는 보호 된 비 정적 데이터 멤버,
가상 함수 및
가상, 개인 또는 보호 된 기본 클래스 가없는 배열 또는 클래스입니다.
[참고 : 집계 초기화는 보호 및 개인 기본 클래스의 멤버 또는 생성자에 대한 액세스를 허용하지 않습니다. — 끝 주]
집합의 요소는 다음과 같습니다.
— 배열, 아래 첨자 순서가 증가하는 배열 요소 또는
— 선언 순서의 직접 기본 클래스, 클래스가 아닌 정적이 아닌 직접 데이터 멤버 신고 순서에 따라 익명의 조합원

무엇이 바뀌 었습니까?

  1. 집계는 이제 비가 상 기본 공개 클래스를 가질 수 있습니다. 또한 기본 클래스를 집계 할 필요는 없습니다. 이들이 집계되지 않으면 목록 초기화됩니다.
struct B1 // not a aggregate
{
    int i1;
    B1(int a) : i1(a) { }
};
struct B2
{
    int i2;
    B2() = default;
};
struct M // not an aggregate
{
    int m;
    M(int a) : m(a) { }
};
struct C : B1, B2
{
    int j;
    M m;
    C() = default;
};
C c { { 1 }, { 2 }, 3, { 4 } };
cout
    << "is C aggregate?: " << (std::is_aggregate&ltC>::value ? 'Y' : 'N')
    << " i1: " << c.i1 << " i2: " << c.i2
    << " j: " << c.j << " m.m: " << c.m.m << endl;

//stdout: is C aggregate?: Y, i1=1 i2=2 j=3 m.m=4
  1. 명시 적 기본 생성자는 허용되지 않습니다.
struct D // not an aggregate
{
    int i = 0;
    D() = default;
    explicit D(D const&) = default;
};
  1. 상속 생성자는 허용되지 않습니다
struct B1
{
    int i1;
    B1() : i1(0) { }
};
struct C : B1 // not an aggregate
{
    using B1::B1;
};


사소한 클래스

사소한 클래스의 정의는 C ++ 14에서 해결되지 않은 몇 가지 결함을 해결하기 위해 C ++ 17에서 재 작업되었습니다. 변경 사항은 본질적으로 기술적이었습니다. 다음은 12.0.6의 새로운 정의입니다 (내부 참조 제거).

사소하게 복사 가능한 클래스는 다음과 같은 클래스입니다.
— 각 복사 생성자, 이동 생성자, 복사 할당 연산자 및 이동 할당 연산자는 삭제되거나 사소한 것입니다
.-삭제되지 않은 복사 생성자, 이동 생성자, 복사 할당 연산자가 하나 이상 있습니다. 또는 이동 할당 연산자 및
— 삭제되지 않은 간단한 소멸자가 있습니다.
사소한 클래스는 사소하게 복사 할 수 있고 하나 이상의 기본 생성자를 갖는 클래스입니다.이 클래스는 모두 사소하거나 삭제되었으며 적어도 하나는 삭제되지 않습니다. [참고 : 특히, 사소한 복사 가능 또는 사소한 클래스에는 가상 기능 또는 가상 기본 클래스가 없습니다 .— 끝 참고]

변경 사항 :

  1. C ++ 14에서 클래스가 사소한 경우 클래스는 사소하지 않은 복사 / 이동 생성자 / 할당 연산자를 가질 수 없었습니다. 그러나 기본 생성자 / 연산자로 암시 적으로 선언 된 경우 , 사소한 것이 아니라 여전히 삭제 것으로 정의 될 수 있습니다. 예를 들어 클래스에 복사 / 이동할 수없는 클래스 유형의 하위 객체가 포함되어 있기 때문입니다. 그러한 사소한, 삭제 된 것으로 정의 된 생성자 / 연산자가 존재하면 전체 클래스가 사소하지 않게됩니다. 소멸자와 비슷한 문제가 발생했습니다. C ++ 17은 그러한 생성자 / 연산자의 존재로 인해 클래스가 사소하게 복사 가능하지 않으므로 사소하지 않으며 사소하게 복사 가능한 클래스에는 사소하고 삭제되지 않은 소멸자가 있어야 함을 명시합니다. DR1734 , DR1928
  2. C ++ 14는 사소하게 복사 가능한 클래스, 즉 사소한 클래스가 모든 복사 / 이동 생성자 / 할당 연산자를 삭제 된 것으로 선언하도록 허용했습니다. 그러나 class와 같은 표준 레이아웃 인 경우을 사용하여 합법적으로 복사 / 이동할 수 있습니다 std::memcpy. 삭제 된 모든 생성자 / 할당 연산자를 정의하여 클래스 작성자는 클래스를 복사 / 이동할 수 없도록 의도했지만 클래스는 여전히 사소하게 복사 가능한 클래스의 정의를 충족했기 때문에 이는 의미 상 모순입니다. 따라서 C ++ 17에서는 사소하게 복사 가능한 클래스에 최소한 하나의 사소하고 삭제되지 않은 (공개적으로 액세스 할 수는 없지만) 복사 / 이동 생성자 / 할당 연산자가 있어야한다는 새 절이 있습니다. N4148 참조 , DR1734
  3. 세 번째 기술 변경은 기본 생성자와 비슷한 문제와 관련이 있습니다. C ++ 14에서 클래스는 암시 적으로 삭제 된 것으로 정의 된 사소한 기본 생성자를 가질 수 있지만 여전히 사소한 클래스입니다. 새로운 정의는 사소한 클래스에 최소한 하나의 사소한 삭제되지 않은 기본 생성자가 있어야 함을 명시합니다. DR1496 참조

표준 레이아웃 클래스

결함 보고서를 해결하기 위해 표준 레이아웃의 정의도 수정되었습니다. 다시 한 번 변경 사항은 기술적 인 부분이었습니다. 다음은 표준 (12.0.7)의 텍스트입니다. 이전과 같이 내부 참조는 생략됩니다.

클래스 S는 다음과 같은 경우 표준 레이아웃 클래스입니다.
— 비표준 레이아웃 클래스 (또는 이러한 유형의 배열) 유형의 비 정적 데이터 멤버
가 없거나 — 가상 기능이없고 가상 기본 클래스가없는 경우
— 모든 비 정적 데이터 멤버에 대해 동일한 액세스 제어가 있으며,
비표준 레이아웃 기본 클래스가 없으며,
특정 유형의 기본 클래스 하위 오브젝트가 하나만
있습니다. 모든 비 정적 데이터 멤버 및 비트 필드가 있습니다. 클래스와 그 기본 클래스가 같은 클래스에서 처음 선언되었으며
기본 클래스 로 유형 M (S) (아래에 정의 된) 집합의 요소가 없습니다 .108
M (X)는 다음과 같이 정의됩니다.
— X가 비 정적 데이터 멤버가없는 (비상 속적으로 상속 될 수있는) 비 유니언 클래스 유형 인 경우 세트 M (X)는 비어 있습니다.
— X가 비 정적 데이터 멤버의 첫 번째 비 정적 데이터 멤버의 유형이 X0 (이 멤버는 익명 공용체 일 수 있음) 인 경우 비 집합 M (X)는 X0과 M (X0)의 요소로 구성됩니다.
— X가 공용체 유형 인 경우 집합 M (X)는 모든 M (Ui)과 모든 Ui를 포함하는 집합의 합집합입니다. 여기서 각 Ui는 X의 비 정적 데이터 멤버의 유형입니다.
— X 인 경우 요소 유형이 Xe 인 배열 유형이며, 세트 M (X)는 Xe와 M (Xe)의 요소로 구성됩니다.
— X가 클래스가 아닌 비 배열 유형 인 경우 세트 M (X)가 비어 있습니다.
[참고 : M (X)는 표준 레이아웃 클래스에서 X에서 0 오프셋이되도록 보장 된 모든 비 기본 클래스 하위 객체의 유형 집합입니다. —end note]
[예 :

struct B { int i; }; // standard-layout class
struct C : B { }; // standard-layout class
struct D : C { }; // standard-layout class
struct E : D { char : 4; }; // not a standard-layout class
struct Q {};
struct S : Q { };
struct T : Q { };
struct U : S, T { }; // not a standard-layout class
—end example]
108) 이렇게하면 클래스 유형이 동일하고 가장 파생 된 동일한 오브젝트에 속하는 두 개의 서브 오브젝트가 동일한 주소에 할당되지 않습니다.

변경 사항 :

  1. 파생 트리에서 오직 하나의 클래스가 비 정적 데이터 멤버를 가지고 있어야한다는 요구 사항이 상속 될 수있는 클래스가 아니라 데이터 멤버가 처음 선언 된 클래스를 참조하고이 요구 사항을 비 정적 비트 필드로 확장 함 . 또한 표준 레이아웃 클래스에는 "어떤 주어진 유형의 기본 클래스 하위 객체가 최대 하나만 있습니다." DR1813 , DR1881 참조
  2. 표준 레이아웃의 정의는 기본 클래스의 유형이 첫 번째 비 정적 데이터 멤버와 동일한 유형이되도록 허용하지 않았습니다. 오프셋 0의 데이터 멤버가 기본 클래스와 동일한 유형을 갖는 상황을 피하기위한 것입니다. C ++ 17 표준은 이러한 유형을 금지하기 위해 "표준 레이아웃 클래스에서 제로 오프셋 (offset)으로 보장되는 모든 비 기본 클래스 하위 오브젝트 유형 세트"에 대한보다 엄격하고 재귀적인 정의를 제공합니다. 기본 클래스의 유형이 아닙니다. DR1672 , DR2120을 참조하십시오 .

참고 : C ++ 표준위원회는 게시 된 C ++ 14 표준에 없지만 새 언어는 C ++ 14에 적용하기 위해 결함 보고서를 기반으로 위의 변경 사항을 적용했습니다. C ++ 17 표준입니다.


C ++ 20의 변경 사항

아직 초기 단계이므로이 답변 중 일부는 나중에 변경 될 수 있습니다. 이 질문의 나머지 분명한 주제에 따라 골재의 의미와 사용은 모든 표준에 따라 계속 변경됩니다. 지평선에는 몇 가지 주요 변경 사항이 있습니다.

사용자 선언 생성자가있는 유형 P1008

C ++ 17에서이 유형은 여전히 ​​집계입니다.

struct X {
    X() = delete;
};

따라서 X{}생성자 호출이 아닌 집계 초기화이기 때문에 여전히 컴파일됩니다. 참고 : 개인 생성자는 언제 개인 생성자가 아닌가?

C ++ 20에서는 다음과 같은 제한이 필요합니다.

사용자 제공 explicit, 또는 상속 된 생성자 없음

사용자 선언 또는 상속 된 생성자 없음

이것은 C ++ 20 작업 초안 에 채택되었습니다 . 어느 쪽도 아니 X여기에 나 C링크 된 문제는 20 ++ C에서 집계되지 않습니다.

이것은 또한 다음 예제에서 요요 효과를 만듭니다.

class A { protected: A() { }; };
struct B : A { B() = default; };
auto x = B{};

C ++ 11/14에서는 기본 클래스로 인해 집계 B아니므 로 액세스 가능한 지점에서 어떤 호출 B{}을 호출 B::B()하는 값 초기화를 수행합니다 A::A(). 이것은 잘 구성되었습니다.

C ++ 17에서는 B기본 클래스가 허용되어 B{}집계가 초기화되어 집계가 초기화되었습니다. 이를 위해서는 copy-list-initializing Afrom 이 필요 {}하지만 컨텍스트 외부에서는 B액세스 할 수 없습니다. C ++ 17에서는 이것이 잘못 형성되었습니다 ( auto x = B();그러나 괜찮을 것입니다).

C ++ 20에서는 위의 규칙 변경으로 B인해 기본 클래스가 아니라 사용자가 선언 한 기본 생성자 때문에 기본적으로 집계가 중단됩니다. 다시 우리는 B의 생성자를 살펴 보았습니다.이 스 니펫은 제대로 구성되었습니다.

괄호로 묶은 값 목록에서 집계 초기화 P960

일반적인 문제는 emplace()집계와 함께 스타일 생성자 를 사용하는 것입니다 .

struct X { int a, b; };
std::vector<X> xs;
xs.emplace_back(1, 2); // error

때문에,하지 작업을 수행 emplace효과적으로 초기화 수행하려고합니다 X(1, 2)유효하지 않습니다. 일반적인 해결책은에 생성자를 추가하는 X것이지만이 제안 (현재 코어를 통해 작동)을 통해 집계는 올바른 일을하는 생성자를 효과적으로 생성하고 일반 생성자처럼 동작합니다. 위의 코드는 C ++ 20에서 그대로 컴파일됩니다 (이 기능이 승인되었다고 가정하면).

집계 P1021에 대한 클래스 템플릿 인수 공제 (CTAD)

C ++ 17에서는 컴파일되지 않습니다.

template <typename T>
struct Point {
    T x, y;
};

Point p{1, 2}; // error

사용자는 모든 집계 템플릿에 대한 자체 추론 가이드를 작성해야합니다.

template <typename T> Point(T, T) -> Point<T>;

그러나 이것은 어떤 의미에서 "명백한 일"이며 기본적으로 보일러 플레이트이므로 언어가이를 대신해 줄 것입니다. 이 변경 사항은 2018 년 11 월 Evolution에 의해 승인되었으므로 위 예제는 C ++ 20으로 컴파일 될 것입니다 (사용자가 제공 한 추론 가이드 필요 없음).

참고 URL : https://stackoverflow.com/questions/4178175/what-are-aggregates-and-pods-and-how-why-are-they-special



반응형