Чому ми не можемо створити тривіально сконструйовані об'єкти за допомогою malloc, якщо тривіальний конструктор за замовчуванням не виконує жодних дій?


14

У мене виникають труднощі в розумінні наступного абзацу, цитованого з cppreference about тривіального конструктора за замовчуванням. Я шукав stackoverflow, але все одно не отримав чіткої відповіді. Тож, будь ласка, допоможіть.

Тривіальний конструктор за замовчуванням - це конструктор, який не виконує жодної дії. Усі типи даних, сумісні з мовою С (типи POD), можуть трибуально конструюватися за замовчуванням. Однак, на відміну від C, об'єкти з тривіальними конструкторами за замовчуванням неможливо створити просто переінтерпретувати відповідним чином вирівняне сховище, наприклад, пам'ять, виділену за допомогою std :: malloc: розташування-new, щоб офіційно ввести новий об'єкт і уникнути можливої ​​не визначеної поведінки.

Зокрема, якщо тривіальний конструктор за замовчуванням нічого не робить, чому ми не можемо переосмислити сховище і зробити вигляд, що існує об'єкт із заданим типом? Чи можете ви надати декілька прикладів можливої ​​невизначеної поведінки, яка це спричинить?


Найважливіша робота компілятора - це не компілювати вихідний код, а відхилити можливо недійсний код. Це не може зробити, коли ви використовуєте malloc ().
Ганс Пасант

6
Причина дуже проста. Чим менше можливостей у програміста робити божевільні речі, тим більше можливостей у компілятора робити шалені речі (агресивні оптимізації).
н. 'займенники' м.

1
З подібних причин, які ви не можете просто *reinterpret_cast<float*>(&someNonFloatObject) = 0.1f;. C ++ має поняття об'єктів та терміни життя об'єкта, вказане на абстрактній машині, і те, що не існує інструкції процесора для створення об'єкта зі сховища, не означає, що на абстрактній машині немає різниці.
Макс Ленгоф

1
@HansPassant Компілятор, який відхиляє весь код, відкидає весь недійсний код. У будь-якому разі, завдання копіювання не має програм, які мають UB.
н. 'займенники' м.

Відповіді:


7

P0593R5 дає такий приклад:

struct X { int a, b; };
X *make_x() {
  X *p = (X*)malloc(sizeof(struct X));
  p->a = 1;
  p->b = 2;
  return p;
}

і пояснює:

У разі компіляції з компілятором C ++ цей код має невизначену поведінку, тому що p-> намагається записати в int subobject об'єкта X, і ця програма ніколи не створювала ні об’єкт X, ні int subbject.

За [intro.object] p1,

Об'єкт створюється визначенням, новим виразом, коли неявна зміна активного члена об'єднання або коли створюється тимчасовий об'єкт.

... і ця програма нічого з цього не робила.

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


1

З причини "чистоти".

Альтернативою та фактичним статус-кво було те, що кожна область пам’яті одночасно міститиме всі об’єкти, що вміщуються у цьому сховищі. Деякі члени комітету переживають статус-кво, і багато людей побоюються думки мати нескінченно багато об'єктів на одному місці (у віртуальному, неініціалізованому стані).

Ніхто ніколи не міг виявити логічну проблему з наявністю нескінченно багатьох об'єктів у регіоні зберігання.

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

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


використання рядкових літералів категорично не допускається у справі. Існує аналогічна проблема CWG щодо type_infoоб'єктів. Ви повідомляли про 'рядкові літерали?
юрист з мови
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.