Що таке агрегати та ПДР та як / чому вони особливі?


548

Цей FAQ стосується агрегатів та ПНД і охоплює такий матеріал:

  • Що таке агрегати ?
  • Що таке POD s (Звичайні старі дані)?
  • Як вони пов'язані?
  • Як і чому вони особливі?
  • Які зміни для C ++ 11?


Чи можна сказати, що мотивація за цими визначеннями приблизно: POD == memcpy'able, Aggregate == агрегат-ініціалізація?
Офек Шилон

Відповіді:


571

Як читати:

Ця стаття досить довга. Якщо ви хочете дізнатися як про агрегати, так і про PODS (Plain Old Data), знайдіть час і прочитайте їх. Якщо вас цікавлять саме агрегати, читайте лише першу частину. Якщо вас цікавлять лише ПОД, то спершу слід ознайомитись із визначенням, наслідками та прикладами агрегатів, а потім ви можете перейти до ПОД, але я все ж рекомендую прочитати першу частину повністю. Поняття агрегатів є важливим для визначення ПДР. Якщо ви виявите помилки (навіть незначні, включаючи граматику, стилістику, форматування, синтаксис тощо), будь ласка, залиште коментар, я відредагую.

Ця відповідь стосується C ++ 03. Інші стандарти C ++ див:

Що таке агрегати і чому вони особливі

Формальне визначення зі стандарту C ++ ( C ++ 03 8.5.1 §1 ) :

Сукупність - це масив або клас (п. 9) без оголошених користувачем конструкторів (12.1), без приватних або захищених нестатичних членів даних (п. 11), без базових класів (п. 10) і без віртуальних функцій (10.3 ).

Отже, добре, давайте розберемо це визначення. Перш за все, будь-який масив - це сукупність. Клас також може бути сукупним, якщо… зачекайте! нічого не сказано про структури чи об'єднання, чи не можуть вони бути агрегатами? Так вони можуть. В 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
else, якщо (m <n)
перші m елементів масиву ініціалізовані з 1 , 2 , ..., a 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для даного типу ( falseдля bool, 0.0для doubleі т.д.). Коли об'єкт типу класу з оголошеним користувачем конструктором за замовчуванням ініціалізується за значенням, його конструктор за замовчуванням викликається. Якщо конструктор за замовчуванням визначено неявно, то всі нестатичні члени рекурсивно ініціалізуються за значенням. Це визначення є неточним і трохи невірним, але воно повинно дати вам основну думку. Посилання не може бути ініціалізовано значенням. Ініціалізація значення для неагрегованого класу може не вдатися, якщо, наприклад, клас не має відповідного конструктора за замовчуванням.

Приклади ініціалізації масиву:

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.i1з 10, y.x.i2з 20, y.i[0]з 20, y.i[1]з 30і y.fініціалізується значення, тобто ініціалізується з 0.0. Захищений статичний член dвзагалі не ініціалізується, оскільки він є static.

Об'єднані об'єднання відрізняються тим, що ви можете ініціалізувати лише їх першого члена дужками. Я думаю, що якщо ви достатньо просунуті в C ++, щоб навіть розглянути можливість використання спілок (їх використання може бути дуже небезпечним і потрібно ретельно продумати), ви можете самі знайти правила для спілок у стандарті :).

Тепер, коли ми знаємо, що особливого в агрегатах, спробуємо розібратися в обмеженнях класів; тобто чому вони там є. Ми повинні розуміти, що ініціалізація за допомогою фігурних дужок означає, що клас - це не що інше, як сума його членів. Якщо присутній визначений користувачем конструктор, це означає, що користувачеві необхідно виконати додаткову роботу для ініціалізації членів, тому ініціалізація дужок була б неправильною. Якщо віртуальні функції присутні, це означає, що об'єкти цього класу мають (у більшості реалізацій) вказівник на так званий vtable класу, який встановлений у конструкторі, тому ініціалізація дужок була б недостатньою. Ви можете визначити решту обмежень аналогічно вправі :).

Так достатньо про агрегати. Тепер ми можемо визначити більш суворий набір типів, до речі, POD

Що таке ПОД і чому вони особливі

Формальне визначення зі стандарту C ++ ( C ++ 03 9 §4 ) :

POD-структура - це сукупний клас, який не має нестатичних членів даних типу не-POD-структура, не-POD-об'єднання (або масив таких типів) або посилання, і не має визначеного користувачем оператора призначення копії та немає визначений користувачем деструктор. Аналогічно, підключення POD - це сукупне об'єднання, яке не має нестатичних членів даних типу не-POD-структура, не-POD-об'єднання (або масив таких типів) або посилання, і не має визначеного користувачем оператора призначення копії і немає визначеного користувачем деструктора. Клас POD - це клас, який є або структурою POD, або об'єднанням POD.

Ого, цей складніше розбирати, чи не так? :) Давайте залишимо профспілки (на тих же підставах, що і вище) і перефразовуємо трохи чіткіше:

Сукупний клас називається POD, якщо він не має визначеного користувачем оператора присвоєння копії та деструктора, і жоден з його нестатичних членів не є класом POD, масивом non-POD або посиланням.

Що означає це визначення? (Чи згадав я POD для стенду Plain 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-класи є найближчими до C-структур. На відміну від них, POD можуть мати функції членів та довільні статичні елементи, але жоден з цих двох не змінює компонування пам'яті об'єкта. Отже, якщо ви хочете написати більш-менш портативну динамічну бібліотеку, яку можна використовувати з C та навіть .NET, вам слід спробувати зробити так, щоб усі експортовані функції приймали та повертали лише параметри POD-типів.

  • Термін експлуатації об'єктів типу не POD починається після закінчення конструктора і закінчується, коли деструктор закінчився. Для класів POD термін служби починається, коли сховище для об'єкта зайнято, і закінчується, коли це сховище звільнене або використане повторно.

  • Для об'єктів типів POD стандартно гарантується, що коли ви memcpyвмістите ваш об'єкт у масив char або непідписаний 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
  • goto заява. Як ви можете знати, незаконно (компілятор повинен помилитися) здійснювати перехід через goto з точки, коли якась змінна ще не була в обсягах, до точки, де вона вже є в області застосування. Це обмеження застосовується лише в тому випадку, якщо змінна не має типу 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 не буде прокладки. Іншими словами, якщо перший член стручка-CLASS A є тип T, ви можете сміливо reinterpret_castвід A*до T*і отримати покажчик на перший елемент , і навпаки.

Список продовжується і продовжується…

Висновок

Важливо зрозуміти, що саме таке ПОД, оскільки багато мовних особливостей, як бачите, поводяться по-різному.


3
Гарна відповідь. Коментарі: "Якщо конструктор за замовчуванням визначено неявно, то всі нестатичні члени рекурсивно ініціалізуються за значенням." та "Ініціалізація значення для неагрегованого класу може не вдатися, якщо, наприклад, клас не має відповідного конструктора за замовчуванням." Неправильно: Ініціалізація значення класу з неявно оголошеним конструктором за замовчуванням не потребує неявно визначеного конструктора за замовчуванням. Таким чином, задано (вставте private:відповідно): struct A { int const a; };тоді A()воно добре сформоване, навіть якщо Aвизначення конструктора за замовчуванням було б неправильним.
Йоханнес Шауб - ліб

4
@Kev: Якщо вам вдасться упакувати ту саму інформацію у більш коротку відповідь, ми б усі радісно проголосували за неї!
sbi

3
@Armen також зауважте, що ви можете зробити кілька відповідей на одне і те ж питання. Кожна відповідь могла містити частину рішення питання. Накручуйте цю річ, на мій погляд, :)
Йоханнес Шауб - ліб

3
Відповідь чудова. Я все ще раз переглядаю цю посаду. До речі про попередження для Visual Studio. "goto statement" для pod приходить з незнанням до компілятора MSVC, як ви вже згадували. Але для оператора switch / case він створює помилку компіляції. Виходячи з цієї концепції, я зробив тест-перевірку: stackoverflow.com/questions/12232766/test-for-pod-ness-in-c-c11/…
bruziuz

2
У крапці, яка починається з "Термін експлуатації об'єктів типу не POD, починається з закінчення конструктора і закінчується, коли деструктор закінчився." остання частина повинна замість цього сказати "коли запускається деструктор".
Quokka

457

Які зміни для C ++ 11?

Агрегати

Стандартне визначення агрегату трохи змінилося, але воно все ще майже те саме:

Сукупність - це масив або клас (п. 9) без наданих користувачем конструкторів (12.1), без дужок або рівних ініціалізаторів для нестатичних членів даних (9.2), без приватних або захищених нестатичних членів даних ( Пункт 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
    };

    Використання цієї функції робить клас більше не сукупним, оскільки він в основному еквівалентний наданню власного конструктора за замовчуванням.

Отже, що таке сукупність, зовсім не змінилося. Це все ж та сама основна ідея, адаптована до нових особливостей.

Що з ПОД?

ПОД пройшли безліч змін. Багато попередніх правил щодо ПДР були розслаблені в цьому новому стандарті, і спосіб визначення цього стандарту був докорінно змінений.

Ідея ПОД полягає у захопленні в основному двох різних властивостей:

  1. Він підтримує статичну ініціалізацію та
  2. Компіляція POD у C ++ дає такий самий макет пам'яті, що і структура, складена у C.

Через це визначення було розділено на два різних поняття: тривіальні класи та класи стандартного компонування , оскільки вони є більш корисними, ніж POD. Зараз стандарт рідко використовує термін POD, віддаючи перевагу більш конкретним тривіальним і стандартним концепціям компонування .

Нове визначення в основному говорить про те, що POD - це клас, який є тривіальним і має стандартний макет, і ця властивість повинна містити рекурсивно для всіх нестатичних членів даних:

Структура POD - це несоюзний клас, який є і тривіальним класом, і класом стандартного макету, і не має нестатичних членів даних типу non-POD structure, un-POD об'єднання (або масив таких типів). Аналогічно, об'єднання POD - це об'єднання, яке є і тривіальним класом, і класом стандартного макета, і не має нестатичних членів даних типу не-POD структура, не-POD об'єднання (або масив таких типів). Клас POD - це клас, який є або структурою POD, або об'єднанням POD.

Давайте детально розглянемо кожну з цих двох властивостей окремо.

Тривіальні класи

Trivial - це перше згадане вище властивість: тривіальні класи підтримують статичну ініціалізацію. Якщо клас можна тривіально скопіювати (набір тривіальних класів), нормально скопіювати його представлення на місці з такими речами 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, що має клас класу (або його масив), конструктор, обраний для копіювання / переміщення цього члена, є тривіальним;

інакше конструктор копіювання / переміщення нетривіальний.

В основному це означає, що конструктор копіювання чи переміщення є тривіальним, якщо він не надається користувачем, у класу немає нічого віртуального, і ця властивість має рекурсивний характер для всіх членів класу та для базового класу.

Визначення тривіального оператора присвоєння копії / переміщення дуже схоже, просто замінивши слово "конструктор" на "оператор присвоєння".

Тривіальний деструктор також має подібне визначення, маючи додаткове обмеження, що воно не може бути віртуальним.

І ще одне подібне правило існує для тривіальних конструкторів за замовчуванням, додаючи, що конструктор за замовчуванням не є тривіальним, якщо у класу є нестатичні члени даних з дужками або рівними ініціалізаторами , які ми бачили вище.

Ось кілька прикладів для очищення всього:

// 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 або об'єднання.

Це ще одне властивість, яке повинно містити рекурсивно для членів та всіх базових класів. І, як зазвичай, не допускаються віртуальні функції або віртуальні базові класи. Це зробить макет несумісним із С.

Тут розслаблене правило полягає в тому, що класи стандартного макета повинні мати всіх нестатичних членів даних з однаковим контролем доступу. Раніше вони повинні були бути загальнодоступними , але тепер ви можете зробити їх приватними або захищеними, якщо всі вони приватні або всі захищені.

При використанні спадщини лише один клас у цілому дереві спадкування може мати нестатичні члени даних, і перший нестатичний член даних не може бути типу базового класу (це може порушити правила псевдоніму), інакше це не є стандартним, макет класу.

Ось як йдеться у стандартному тексті визначення:

Клас стандартного макета - це клас, який:

- не має членів нестатичних даних типу нестандартного класу класу (або масиву таких типів) або посилань,

- не має віртуальних функцій (10.3) і не має віртуальних базових класів (10.1),

- має однаковий контроль доступу (п. 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 окремо (якщо це лише одне з тривіального або стандартного макета).

Стандартна бібліотека має риси перевірки цих властивостей у заголовку <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;

2
Ви можете, будь ласка, розробити такі правила: а) класи стандартного макета повинні мати всіх нестатичних членів даних з однаковим контролем доступу; б) лише один клас у всьому дереві спадкування може мати нестатичні члени даних, і перший нестатичний член даних не може бути типу базового класу (це може порушити правила псевдоніму). Особливо які причини для них? Для подальшого правила, чи можете ви навести приклад розбиття псевдоніму?
Андрій Тиличко

@AndyT: Дивіться мою відповідь. Я намагався відповісти, наскільки мені відомо.
Нікол Болас

5
Можливо, хочеться оновити це для C ++ 14, що видалило для агрегатів вимогу "без дужок або рівних ініціалізаторів".
ТК

@TC дякую за голову. Я незабаром знайду ці зміни та оновлю їх.
Р. Мартіньо Фернандес

1
Що стосується псевдоніму: Існує правило компонування C ++, що якщо клас C має (порожню) базу X, а перший член даних C - тип X, то цей перший член не може бути таким же зрушеним, як базовий X; перед цим він отримує байт-манекен для підкладки, якщо це потрібно, щоб уникнути цього. Наявність двох екземплярів X (або підкласу) за однією адресою може порушити речі, які потребують розрізнення різних примірників за їх адресами (порожній екземпляр не має нічого іншого, щоб розрізнити його ...). У будь-якому випадку потрібно вкласти цей байт розбиття "сумісний макет".
greggo

106

Що змінилося для C ++ 14

Для посилання ми можемо звернутися до стандарту проекту C ++ 14 .

Агрегати

Це висвітлено у розділі 8.5.1 Агрегати, що дає нам таке визначення:

Сукупність - це масив або клас (п. 9) без наданих користувачем конструкторів (12.1), без приватних або захищених нестатичних членів даних (п. 11), без базових класів (п. 10) і без віртуальних функцій (10.3 ).

Єдина зміна - додавання ініціалізаторів членів класу не робить клас несукупним. Отже, наступний приклад агрегатизації C ++ 11 для класів з ініціалізаторами-членами в темпі :

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

не було сукупністю в C ++ 11, але це в C ++ 14. Ця зміна охоплюється у N3605: Ініціалізатори та агрегати учасників , які мають такий конспект:

Б'ярн Струструп та Річард Сміт порушили питання про сукупну ініціалізацію та ініціалізатори членів, які не працюють разом. У цьому документі пропонується виправити цю проблему шляхом прийняття запропонованого Смітом формулювання, яке знімає обмеження, що агрегати не можуть мати ініціалізатори членів.

ПОД залишається таким же

Визначення структури POD ( звичайні старі дані ) висвітлено в розділі 9 Класи, який говорить:

Структура POD 110 - це несоюзний клас, який є і тривіальним класом, і класом стандартного макету, і не має нестатичних членів даних типу non-POD Structure, non-POD об'єднання (або масив таких типів). Аналогічно, об'єднання POD - це об'єднання, яке є і тривіальним класом, і класом стандартного макета, і не має нестатичних членів даних типу не-POD структура, об'єднання, що не є POD (або масив таких типів). Клас POD - це клас, який є або структурою POD, або об'єднанням POD.

що є тим же формулюванням, що і C ++ 11.

Зміни стандартного макета для C ++ 14

Як зазначається в коментарях, струк покладається на визначення стандартного макета, і це змінилося для C ++ 14, але це було через повідомлення про дефекти, які були застосовані до C ++ 14 після факту.

ДР було:

Тож стандартний макет пішов із цього попереднього C ++ 14:

Клас стандартного макета - це клас, який:

  • (7.1) не має нестатичних членів даних типу нестандартного класу класу (або масиву таких типів) або посилань,
  • (7.2) не має віртуальних функцій ([class.virtual]) і не має віртуальних базових класів ([class.mi]),
  • (7.3) має однаковий контроль доступу (пункт [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. - кінцева примітка]
    • (3.7.1) Якщо X - несоюзний тип класу, який не має (можливо, успадкованих) нестатичних членів даних, безліч M (X) порожній.
    • (3.7.2) Якщо X - несоюзний тип класу з нестатичним членом даних типу X0, який має нульовий розмір, або є першим нестатичним членом даних X (де зазначений член може бути анонімним об'єднанням ), множина M (X) складається з X0 та елементів M (X0).
    • (3.7.3) Якщо X - тип з'єднання, множина M (X) - це об'єднання всіх M (Ui) і множина, що містить усі Ui, де кожен Ui - тип i-го нестатичного члена даних X .
    • (3.7.4) Якщо X - тип масиву з елементом типу Xe, множина M (X) складається з Xe та елементів M (Xe).
    • (3.7.5) Якщо X - некласовий, не масивний тип, множина M (X) порожня.

4
Існує пропозиція , щоб агрегати мати базовий клас до тих пір , як це по умовчанням constructable см N4404
Шафік Ягмур

в той час як POD може залишатися колишнім, C ++ 14 StandardLayoutType, що є вимогою для POD, змінився відповідно до cppref: en.cppreference.com/w/cpp/named_req/StandardLayoutType
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@CiroSantilli 新疆 改造 中心 六四 事件 法轮功 дякую, я не знаю, як я пропустив їх, я спробую оновити протягом наступних двох днів.
Шафік Ягмур

Повідомте мене, чи можете ви запропонувати приклад, який є POD у C ++ 14, а не в C ++ 11 :-) Я почав детальний список прикладів за адресою: stackoverflow.com/questions/146452/what- are-pod-types-in-c /…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@CiroSantilli 新疆 改造 中心 六四 事件 法轮功, так що тут сталося, якщо ми подивимось опис стандартного макета в C ++ 11 і C ++ 14, які вони відповідають. Ці зміни застосовуються через звіти про дефекти назад до C ++ 14. Тож коли я писав це спочатку, це було правильно :-p
Шафік Ягмур

47

Ви можете, будь ласка, розробити такі правила:

Я спробую:

a) Класи стандартного макета повинні мати всі нестатичні члени даних з однаковим контролем доступу

Це просто: все не статичні дані повинні все бути public, privateабо protected. Ви не можете мати деяких publicі деякихprivate .

Міркування для них переходять до міркувань про те, щоб розрізняти "стандартний макет" і "не стандартний макет" взагалі. А саме, щоб надати компілятору свободу вибирати, як скласти речі в пам'ять. Йдеться не лише про vtable покажчики.

Ще коли вони стандартизували C ++ у 98 році, їм довелося в основному передбачити, як люди його застосовуватимуть. Хоча вони мали досить багато досвіду впровадження з різними смаками C ++, вони не були впевнені в речах. Тож вони вирішили бути обережними: надайте компіляторам якомога більше свободи.

Ось чому визначення POD у С ++ 98 настільки суворе. Це дало компіляторам C ++ велику широту розміщення членів для більшості класів. В основному, типи POD призначені для особливих випадків, про що ви спеціально писали з причини.

Коли над C ++ 11 працювали, вони мали набагато більше досвіду роботи з компіляторами. І вони зрозуміли, що ... Співавтори компіляторів C ++ справді ледачі. Вони мали всю цю свободу, але цього не робили нічого з цим .

Правила стандартного планування більш-менш кодифікують загальну практику: більшості компіляторів насправді не довелося багато змінювати, щоб взагалі щось реалізувати (за винятком, можливо, деяких матеріалів для відповідних ознак типу).

Зараз, коли мова зайшла про public/ private, все інакше. Свобода змінити порядок членів команди publicvs.private насправді може мати значення для компілятора, особливо у налагодженні складок. А оскільки суть стандартного макета полягає в сумісності з іншими мовами, ви не можете мати макет відрізнятись від налагодження проти випуску.

Тоді є той факт, що це не дуже шкодить користувачеві. Якщо ви робите інкапсульований клас, шанси на те, що всі ваші учасники даних все privateодно будуть . Як правило, учасників публічних даних не виставляють на цілком капсульовані типи. Тож це буде лише проблемою для тих небагатьох користувачів, які хочуть зробити це, хто хоче цього поділу.

Тож це не велика втрата.

б) лише один клас у всьому дереві спадкування може мати нестатичні члени даних,

Причина цього повертається до того, чому вони знову стандартизували стандартний макет: звичайна практика.

Там немає НЕ звичайної практики , коли справа доходить до двох членів дерева спадкування , що на насправді зберігати речі. Одні ставлять базовий клас перед похідним, інші роблять це іншим способом. Яким способом ви замовляєте членів, якщо вони походять з двох базових класів? І так далі. Компілятори сильно розходяться з цих питань.

Крім того, завдяки правилу нуль / один / нескінченність, коли ти кажеш, що можеш мати два класи з членами, ти можеш сказати стільки, скільки хочеш. Для цього потрібно додати багато правил компонування, як впоратися з цим. Ви повинні сказати, як працює багатократне успадкування, які класи передають свої дані перед іншими класами і т. Д. Це дуже багато правил для дуже невеликої матеріальної вигоди.

Ви не можете зробити все, що не має віртуальних функцій та стандартного макета конструктора за замовчуванням.

і перший нестатичний член даних не може бути типу базового класу (це може порушити правила псевдоніму).

Я не можу реально говорити з цим. Я недостатньо освічений в правилах збитку C ++, щоб справді його зрозуміти. Але це має щось спільне з тим, що базовий член поділить ту саму адресу, що і сам базовий клас. Це є:

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

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

І це, мабуть, суперечить правилам C ++. Якимось чином.

Тим НЕ менше, вважають це: наскільки корисним може маючи можливість зробити це коли - небудь на насправді бути? Оскільки лише один клас може мати нестатичні члени даних, то він Derivedповинен бути цим класом (оскільки він має Baseяк член). Тому Base повинно бути порожнім (даних). А якщо Baseпорожній, як і базовий клас ... навіщо взагалі мати його членами даних?

Оскільки Baseвін порожній, він не має стану. Таким чином, будь-які нестатичні функції членів будуть робити те, що вони роблять, спираючись на їх параметри, а не на їх thisвказівник.

Тож знову: без великих втрат.


Дякую за пояснення, це дуже допомагає. Ймовірно, незважаючи на те , що вони одного типу, і вони є одним static_cast<Base*>(&d)і &d.bтим же Base*типом, вони вказують на різні речі. Будь ласка, виправте мене.
Андрій Тиличко

1
і чому, якщо тільки один клас може мати нестатичні члени даних, то це Derivedповинен бути цей клас?
Андрій Тиличко

3
@AndyT: Для того, щоб Derivedперший член був його базовим класом, він повинен мати дві речі: базовий клас та член . А оскільки лише один клас в ієрархії може мати членів (і все ще бути стандартним макетом), це означає, що його базовий клас не може мати членів.
Нікол Болас

3
@AndyT, Так, ти, правда, правильний, IME, щодо правила псевдоніму. Два чіткі екземпляри одного типу повинні мати різні адреси пам'яті. (Це дозволяє відстежувати ідентичність об'єкта з адресами пам'яті.) Базовий об'єкт і перший похідний член - це різні екземпляри, тому вони повинні мати різні адреси, що змушує додавати оббивки, що впливає на макет класу. Якби вони були різного типу, це не мало значення; Об'єкти з різними типами дозволяють мати однакову адресу (наприклад, клас та його перший член даних).
Адам Х. Петерсон

46

Зміни в C ++ 17

Завантажте остаточний проект проекту C ++ 17 International Standard тут .

Агрегати

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<C>::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 ++ 17 для усунення декількох дефектів, які не були вирішені в C ++ 14. Зміни мали технічний характер. Ось нове визначення на рівні 12.0.6 (внутрішні посилання зникли):

Тривіально скопіюваний клас - це клас:
- де кожен конструктор копіювання, конструктор переміщення, оператор присвоєння копії та оператор присвоєння переміщення видалено або тривіально,
- який має щонайменше один не видалений конструктор копій, конструктор переміщення, оператор призначення копії, або перемістити оператор присвоєння, і
- у якого є тривіальний, не видалений деструктор.
Тривіальний клас - це клас, який можна скопіювати тривіально і має один або кілька конструкторів за замовчуванням, всі вони або тривіальні, або видалені, і принаймні один з них не видаляється. [Примітка. Зокрема, у тривіально копіюваного або тривіального класу немає віртуальних функцій або віртуальних базових класів. Кінцева примітка]

Зміни:

  1. Під C ++ 14, щоб клас був тривіальним, клас не міг мати жодних операторів копіювання / переміщення конструктора / призначення, які були нетривіальними. Однак тоді неявно оголошений конструктор / оператор за умовчанням може бути нетривіальним і все-таки визначений як видалений, оскільки, наприклад, клас містив субект типу класу, який неможливо скопіювати / перемістити. Наявність такого конструктора / оператора з нетривіальною ознакою, що визначається як видалений, призведе до того, що весь клас буде нетривіальним. Аналогічна проблема була і з деструкторами. C ++ 17 уточнює, що наявність такого конструктора / операторів не спричиняє клас нетривіально копіюваного, отже, нетривіального, і що тривіально копіюваний клас повинен мати тривіальний, не видалений деструктор. DR1734 , DR1928
  2. C ++ 14 дозволив класу "тривіально копіювати", отже, тривіального класу, кожен оператор копіювання / переміщення / оператор присвоєння оголосив як видалений. Якщо такий клас, як і стандартний макет, він, однак, міг би бути законно скопійований / переміщений 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 - тип i-го нестатичного члена даних X.
- Якщо X - це тип масиву з елементом типу Xe, безліч M (X) складається з Xe і елементів M (Xe).
- Якщо X - це некласовий тип, не масив, множина M (X) порожня.
[Примітка: M (X) - це набір типів усіх суб'єктів, що не є базовим класом, які гарантуються в класі стандартного компонування таким чином, що вони мають нульове зміщення в X. —закінчити примітку]
[Приклад:

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
—Закінчити приклад]
108) Це гарантує, що два суб’єкти, які мають один і той же тип класу і належать до одного і того ж самого похідного об'єкта, не виділяються за однією адресою.

Зміни:

  1. Уточнено, що вимога, що лише один клас у дереві деривації "має" нестатичні члени даних, відноситься до класу, де такі члени даних вперше оголошуються, а не до класів, де вони можуть бути успадковані, і поширила цю вимогу на нестатичні бітові поля . Також було уточнено, що клас стандартного макету "має щонайбільше один базовий клас суб'єкта будь-якого даного типу". Див. DR1813 , DR1881
  2. Визначення стандартного макета ніколи не дозволяло типу будь-якого базового класу бути того ж типу, що і перший нестатичний член даних. Потрібно уникати ситуації, коли член даних при зміщенні нуля має той самий тип, що і будь-який базовий клас. Стандарт C ++ 17 надає більш жорстке, рекурсивне визначення "набору типів усіх суб'єктів, що не є базовим класом, які гарантуються в класі стандартної компонування з нульовим зміщенням", щоб заборонити такі типи від типу будь-якого базового класу. Див. DR1672 , DR2120 .

Примітка: Комітет стандартів C ++ призначив вищезазначені зміни, засновані на повідомленнях про дефекти, застосувати до C ++ 14, хоча нова мова відсутня у опублікованому стандарті C ++ 14. Це в стандарті C ++ 17.


Примітка. Щойно я оновив свою відповідь, стандартні дефекти зміни макета мають статус CD4, що означає, що вони фактично застосовані до C ++ 14. Ось чому моя відповідь не включала їх, але це сталося після того, як я написав свою відповідь.
Шафік Ягмур

Зауважте, я розпочав щедро це питання.
Шафік Ягмур

Дякую @ShafikYaghmour Я перегляну статус звітів про дефекти та відповідно зміню свою відповідь.
ThomasMcLeod

@ShafikYaghmour, Після деякого огляду процесу C ++ 14, і мені здається, що в той час, коли ці ДР були "прийняті" на зустрічі Rapperswil у червні 2014 року, мова відбулася в лютому 2014 року в Іссаквах, це стало C ++ 14. Див. Isocpp.org/blog/2014/07/trip-report-summer-iso-c-meeting "відповідно до правил ISO ми офіційно не затверджували жодних змін до робочого документу C ++". Я щось пропускаю?
ThomasMcLeod

Вони мають статус "CD4", що означає, що вони повинні застосовуватися в режимі C ++ 14.
Шафік Ягмур

14

Що зміниться в

Після решти чіткої теми цього питання значення та використання агрегатів продовжує змінюватися з кожним стандартом. На горизонті відбувається кілька ключових змін.

Типи з оголошеними користувачем конструкторами P1008

У C ++ 17 цей тип як і раніше є сукупним:

struct X {
    X() = delete;
};

А значить, X{}все ще компілюється, тому що це сукупна ініціалізація - не виклик конструктора. Дивіться також: Коли приватний конструктор не є приватним конструктором?

У C ++ 20 обмеження зміниться:

не надані користувачем explicitабо успадковані конструктори

до

немає оголошених користувачем або успадкованих конструкторів

Це було прийнято до робочого проекту C ++ 20 . Ні Xтут, ні Cв пов'язаному питанні не будуть сукупності в C ++ 20.

Це також спричинить йо-йо ефект із наступного прикладу:

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{}агрегатизацію. Для цього потрібна ініціалізація копіювання списку Aз {}, але поза межами контексту 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, але за допомогою цієї пропозиції (в даний час він працює через Core) агрегати будуть ефективно синтезувати конструктори, які роблять правильно - і поводяться як звичайні конструктори. Вищевказаний код буде складено як є в C ++ 20.

Вирахування аргументів шаблону класу (CTAD) для агрегатів P1021 (конкретно P1816) )

У C ++ 17 це не компілюється:

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

Point p{1, 2}; // error

Користувачі повинні написати власний посібник з вирахування для всіх сукупних шаблонів:

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

Але оскільки це в певному сенсі "очевидна справа", і це в основному лише котельня, мова зробить це за вас. Цей приклад буде складено в C ++ 20 (без потреби в наданому користувачем посібнику щодо вирахування).


Хоча я висловлюся на користь, але це ще трохи рано додавати це, я не знаю нічого серйозного, що може змінити це, перш ніж C ++ 2x буде зроблено.
Шафік Ягмур

@ShafikYaghmour Так, напевно, БУДЬ занадто рано. Але з огляду на те, що SD виявився крайнім терміном для нових мовних особливостей, про це я знаю лише два під час польоту, - найгірший випадок, я просто заблокував видалення одного з цих розділів пізніше? Я щойно побачив питання, яке було активне з щедротою, і подумав, що настав час прослухати, перш ніж я забуду.
Баррі

Я розумію, мене кілька разів спокушали подібні випадки. Я завжди переживаю, що щось серйозне зміниться, і мені доведеться переписати це.
Шафік Ягмур

@ShafikYaghmour Схоже, що тут нічого не зміниться :)
Баррі

Я сподіваюся, що це оновлено зараз, коли вже випущено C ++ 20
Noone AtAll
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.