Обработка памяти, возвращаемой оператором new (sizeof (T) * N) как массив

В C можно выделять динамические массивы с помощью malloc(sizeof(T) * N), а затем использовать арифметику указателей для получения элементов со смещением i в этом динамическом массиве.

В C++ можно сделать то же самое, используя operator new() так же, как malloc(), а затем разместить новый (например, решение для пункта 13 можно увидеть в книге Херба Саттера «Исключительный C++: 47 инженерных головоломок, проблем программирования и решений») . Если у вас его нет, краткое изложение решения этого вопроса будет следующим:

T* storage = operator new(sizeof(T)*size);

// insert element    
T* p = storage + i;
new (p) T(element);

// get element
T* element = storage[i];

Для меня это выглядело законным, поскольку я прошу кусок памяти с достаточным объемом памяти для хранения N выровненных элементов размера = sizeof(T). Поскольку sizeof(T) должен возвращать размер элемента, который выровнен, и они размещаются один за другим в блоке памяти, использование арифметики указателей здесь нормально.

Однако затем мне указали на такие ссылки, как: http://eel.is/c++draft/expr.add#4 или http://eel.is/c++draft/intro.object#def:object и утверждая, что в C++ operator new() не возвращает объект массива, поэтому арифметика указателя над тем, что он вернул, и использование его в качестве массива является неопределенным поведением в отличие от ANSI C.

Я не настолько хорош в таких низкоуровневых вещах, и я действительно пытаюсь понять, читая это: https://www.ibm.com/developerworks/library/pa-dalign/ или это: http://jrruethe.github.io/blog/2015/08/23/placement-new/, но я все еще не могу понять, был ли Саттер просто неправ?

Я понимаю, что alignas имеет смысл в таких конструкциях, как:

alignas(double) char array[sizeof(double)];

(c) http://georgeflanagin.com/alignas.php

Если кажется, что массив не находится в границах double (возможно, после char в структуре, работающей на 2-байтовом процессоре чтения).

Но это другое дело - я запросил память из кучи / свободного хранилища, особенно запросил оператор new для возврата памяти, которая будет содержать элементы, выровненные по sizeof(T).

Подводя итог, если это был TL; DR:

  • Можно ли использовать malloc() для динамических массивов в C++?
  • Можно ли использовать operator new() и новое размещение для динамических массивов в более старом C++, в котором нет ключевого слова alignas?
  • Не определено ли поведение арифметики указателя при использовании в памяти, возвращаемой operator new()?
  • Саттер советует код, который может сломаться на какой-нибудь старинной машине?

Извините, если это глупо.

Можно ли использовать malloc () для динамических массивов в C? - вы хотели написать C++?
Swordfish 23.11.2018 20:08

да. И я хотел понять, в чем разница между malloc в C и C++ в этом случае. Поскольку я давно видел, что оператор new () реализован в терминах malloc () в некоторых заголовках C++ в какой-то версии GCC, поэтому оператор new () будет просто эквивалентен malloc (), поэтому могу ли я использовать над ним арифметику указателя в C++ без выравнивания? Я совершенно запуталась. :(

xor256 23.11.2018 20:11

@ xor256 Я полагаю, что реализация может использовать неопределенное поведение, если она дает больше гарантий для себя. Поэтому то, использует ли GCC что-то в своей реализации библиотеки (даже при условии, что она не содержит ошибок и придерживается стандарта), ничего не говорит вам о том, определено ли это поведение в стандарте.

user10605163 23.11.2018 20:14

Вы можете поместить сюда весь код? Потому что представленный вами код даже не компилируется (по крайней мере, в первой строке отсутствует приведение).

geza 23.11.2018 20:20

Для арифметики указателя вам не нужен массив, единственное, что вы должны быть осторожны, чтобы избежать неопределенного поведения, - это не перемещаться за конец указателя (один за вашим выделенным размером).

yyny 23.11.2018 20:23

Мне не удается найти то, что, по моему мнению, является релевантным сообщением. Однако я думаю, что это нормально, потому что я почти уверен, что в стандарте говорится, что один объект рассматривается как массив из одного элемента по отношению к арифметике указателей.

Galik 23.11.2018 20:34

@Galik Там написано, что в eel.is/c++draft/expr.add#footnote-85. Я не думаю, что указатель считается указывающим на один объект. В выделенной памяти не было построено ни одного объекта.

user10605163 23.11.2018 20:41

@eukaryota Да, мне кажется, я неправильно понял вопрос. Неужели вы не думаете, что выражение (possibly-hypothetical) можно извлечь из стандартной формулировки?

Galik 23.11.2018 20:55

Да, как я понимаю, в этом коде действительно есть UB. Но, на мой взгляд, исправлять нужно стандарт, а не код Херба. Было бы интересно узнать, почему у нас есть такое ограничивающее правило для арифметики указателей.

geza 23.11.2018 21:00

@geza Может быть, это потому, что определение объект было недавно изменено на более ограничительное? Раньше даже неинициализированная память была объектом.

Galik 23.11.2018 21:01

@Galik Я точно не знаю, но в моем любительском чтении possibly-hypothetical относится к hypothetical x[n] после фактического массива, и соответствующий if из 4.2 также не использует его.

user10605163 23.11.2018 21:01

@eukaryota Я думаю, это означает в целях арифметики, пока память выделена и теоретически может стать объектом, тогда арифметика работает. Но трудно быть уверенным

Galik 23.11.2018 21:03

@Galik: Насколько я помню, в C++ 98 было то же правило, и оно означало то же самое (в том смысле, что код Херба никогда не был четко определен).

geza 23.11.2018 21:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
10
13
682
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Проблема арифметики указателя на выделенную память, как в вашем примере:

T* storage = static_cast<T*>(operator new(sizeof(T)*size));
// ...
T* p = storage + i;  // precondition: 0 <= i < size
new (p) T(element);

технически неопределенное поведение известно давно. Это означает, что std::vector не может быть реализован с четко определенным поведением только как библиотека, но требует дополнительных гарантий от реализации, помимо тех, которые содержатся в стандарте.

Комитет по стандартам определенно не намеревался сделать std::vector невыполнимой. Саттер, конечно, прав в том, что такой код предназначена должен быть четко определен. Формулировка стандарта должна это отражать.

P0593 - это предложение, которое, если оно будет принято в стандарте, может решить эту проблему. Между тем, можно продолжать писать код, подобный приведенному выше; ни один крупный компилятор не будет рассматривать его как UB.

Редактировать: Как указано в комментариях, я должен был сказать, что когда я сказал, что storage + i будет четко определен в P0593, я предполагал, что элементы storage[0], storage[1], ..., storage[i-1] уже созданы. Хотя я не уверен, что понимаю P0593 достаточно хорошо, чтобы сделать вывод, что он также не охватывает случай, когда эти элементы не имел уже созданы.

Хм, а почему тут P0593 актуален? T может быть любого типа. Думаю, это предложение не решит эту проблему.

geza 23.11.2018 20:32
std::vector не является нереализуемым. Возможно, это нереализуемый в коде пользователя. Стандартная библиотека не может быть реализована в переносимом коде, тем более в коде, написанном пользователем. Это одна из причин, по которой он поставляется с компилятором - он может использовать известное поведение этого компилятора и целевой ОС.
Pete Becker 23.11.2018 20:42

@PeteBecker Вот что я имел в виду. std::vector не должен был быть реализован в пользовательском коде.

Brian Bi 23.11.2018 20:50

@geza Я сделал предположение (которое, вероятно, мне следовало заявить), что все storage[0], storage[1], ..., storage[i-1] уже созданы. В этом случае P0593 подразумевает, что объект массива с элементами i создается неявно, а storage + i находится за концом и, следовательно, четко определен. P0593 указывает, что он предназначен для работы с массивами любого типа.

Brian Bi 23.11.2018 20:55

Я имел в виду, что P0593 касается типов, которые автор называет «неявными типами времени жизни». Таким образом, это предложение не касается всех типов, оно не может быть общим решением этой проблемы. Но и в этом я не уверен на 100% :)

geza 23.11.2018 21:05

Но в чем причина УБ? Это не арифметика указателей, а этот std :: bless what is? Размещение новое? Можете ли вы свести это к элементарным блокам?

xor256 23.11.2018 21:08

А как насчет арифметики на (uint8_t *)storage? Разве это не было бы четко определенным, позволяющим четко определить реализацию vector?

HolyBlackCat 23.11.2018 21:10

@HolyBlackCat: Нет. Насколько я знаю, вам нужно подключить reinterpret_cast к uintptr_t и выполнять там арифметические операции. Это, конечно, поведение, определяемое реализацией

geza 23.11.2018 21:13

@geza Он говорит, что тип массива типа элемента любой является неявным типом времени жизни (независимо от того, является ли тип элемента неявным типом времени жизни). Потому что, если у вас уже есть группа объектов типа T, выстроенных в память, то для создания массива T не требуется никакого дополнительного кода.

Brian Bi 23.11.2018 21:16

Спасибо, это действительно имеет смысл! А теперь пора в 35-й раз перечитать это предложение :)

geza 23.11.2018 21:18

Я не уверен, следует ли мне создавать новый вопрос, позвольте мне задать его здесь. Предположим, мы не используем значение, возвращаемое new. Можем ли мы использовать storage или p для доступа к элементам? Или надо сначала его std::launder?

Evg 02.12.2019 19:49

@Evg Хороший вопрос. Я думаю, что требуется std::launder - в противном случае исходный указатель останется недопустимым значением указателя, поскольку в стандарте нет положения, чтобы он автоматически начинал указывать на вновь созданный объект. Но я не уверен в этом.

Brian Bi 02.12.2019 20:10
Хотя я не уверен, что понимаю P0593 достаточно хорошо, чтобы сделать вывод, что он также не охватывает случай, когда эти элементы еще не были созданы. В противном случае это было бы невозможно: buf_end_size = newbuf + sizeof(T) * size();. Здесь арифметика указателя используется для получения указателя, который переходит в конец массива объектов, которые еще не существуют. Я ошибся?
skypjack 09.06.2021 16:42

Вы можете сделать это с помощью «старомодного» malloc, который дает вам блок памяти, который выполняет наиболее жесткое выравнивание на соответствующей платформе (например, long long double). Таким образом, вы сможете поместить любой объект в такой буфер без нарушения каких-либо требований к выравниванию.

Учитывая это, вы можете использовать новое размещение для массивов вашего типа на основе такого блока памяти:

struct MyType {
    MyType() {
        cout << "in constructor of MyType" << endl;
    }
    ~MyType() {
        cout << "in destructor of MyType" << endl;
    }
    int x;
    int y;
};

int main() {

    char* buffer = (char*)malloc(sizeof(MyType)*3);
    MyType *mt = new (buffer)MyType[3];

    for (int i=0; i<3; i++)  {
        mt[i].~MyType();
    }
    free(mt);
}

Обратите внимание, что - как всегда с размещением new - вам придется позаботиться о явном вызове деструкторов и освобождении памяти на отдельном шаге; Вы не должны использовать функции delete или delete[], которые объединяют эти два шага и тем самым освобождают память, которой они не владеют.

Но могу ли я использовать в вашем примере оператор new () вместо malloc ()?

xor256 23.11.2018 21:09

Насколько я понимаю, новое размещение массива может потребовать неуказанных накладных расходов на память. Таким образом, определение поведения в этом коде зависит от реализации. См. stackoverflow.com/questions/8720425/…

user10605163 23.11.2018 21:20

@eukaryota обратите внимание, что в примерах Sutter и open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html не используется размещение массива new. Они используют новое размещение только для каждого элемента, который они создают внутри блока памяти.

xor256 23.11.2018 21:40

@ xor256 Да, в этом ответе я конкретно имею в виду пример кода. Новое размещение без массива не может иметь эти накладные расходы.

user10605163 23.11.2018 21:42

Ко всем широко используемым современным posix-совместимым системам, то есть к Windows, Linux (и Android и т. д.), И MacOSX применимо следующее:

Is it possible to use malloc() for dynamic arrays in C++?

Да, это. Использование reinterpret_cast для преобразования результирующего void* в желаемый тип указателя является наилучшей практикой, и в результате получается динамически распределенный массив, подобный этому: type *array = reinterpret_cast<type*>(malloc(sizeof(type)*array_size);Будьте осторожны, в этом случае конструкторы не вызываются для элементов массива, поэтому это все еще неинициализированное хранилище, независимо от того, что такое type. Деструкторы не вызываются, когда free используется для освобождения памяти.


Is it possible to use operator new() and placement new for dynamic arrays in older C++ which has no alignas keyword?

Да, но вам нужно знать о выравнивании в случае размещения new, если вы вводите его в пользовательские местоположения (то есть те, которые не поступают из malloc / new). Обычный оператор new, так же как и malloc, предоставит собственные области памяти, выровненные по словам (по крайней мере, всякий раз, когда размер выделения> = wordize). Этот факт, а также тот факт, что макеты и размеры структуры определены таким образом, чтобы выравнивание учитывалось должным образом, вам не нужно беспокоиться о выравнивании массивов dyn, если используется malloc или new. Можно заметить, что размер слова иногда значительно меньше, чем самый большой встроенный тип данных (обычно это long double), но он должен быть выровнен таким же образом, поскольку выравнивание касается не размера данных, а разрядности адресов на шина памяти для разных размеров доступа.


Is pointer arithmetic undefined behavior when used over memory returned by operator new()?

Нет, пока вы соблюдаете границы памяти процесса - с этой точки зрения new в основном работает так же, как malloc, более того, new фактически вызывает malloc в подавляющем большинстве реализаций, чтобы получить требуемую область. На самом деле арифметика указателей как таковая никогда не бывает недействительной. Однако результат арифметического выражения, оценивающего указатель, может указывать на место за пределами разрешенных областей, но это не ошибка арифметики указателя, а ошибочное выражение.


Is Sutter advising code which might break on some antique machine?

Я так не думаю, если используется правильный компилятор. (не компилируйте инструкции avr или mov с 128-битной памятью в двоичный файл, предназначенный для работы на 80386) Конечно, на разных машинах с разным объемом памяти и разметкой один и тот же буквальный адрес может иметь доступ к областям с разным назначением / статусом / существованием, но зачем вам использовать буквальные адреса, если вы не пишете код драйвера для определенного оборудования? ... :)

Но почему в ответной ссылке @Brian для того же примера, что и Саттерс (за исключением деталей реализации) самодельного вектора, говорится, что: «На практике этот код работает с рядом существующих реализаций, но в соответствии с В объектной модели C++ неопределенное поведение возникает в точках #a, #b, #c, #d и #e, потому что они пытаются выполнить арифметические операции с указателями в области выделенной памяти, которая не содержит объект массива. "? (c) open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html

xor256 23.11.2018 21:11

Что именно вы имеете в виду под widely used posix-based systems ?. Вы основываете свои ответы на гарантии стандартизации или внедрения?

user10605163 23.11.2018 21:31

@eukaryota ни один из них, но чистый опыт. Применимо как минимум к последним версиям Windows, Linux (и Android ofc) и Mac OSX.

Géza Török 24.11.2018 00:31

@ xor256 Это просто неправда ... каждое выделение в конечном итоге приведет к вызову malloc, а реализация malloc обеспечивается библиотекой платформы C, которая должен сама решает эти проблемы, иначе вы не сможете использовать массивы структур в C либо ...

Géza Török 24.11.2018 10:06

Ой, народ ... вы никогда не видели ассемблерного кода? Указатели - это простые целые числа без знака, безумный страх использовать их в арифметике ...

Géza Török 24.11.2018 10:15

@ GézaTörök - это не страх перед указателями или арифметикой над ними, это страх сложности C++, он все время меняется и не может понять, что написано в стандарте (как и в налоговых правилах). Проблема должна возникнуть, по крайней мере, для разработчиков компилятора, поскольку он существует, поэтому, хотя с точки зрения пользователя C++ ответы на мой исходный вопрос полностью удовлетворяют, с точки зрения любопытного человека я все еще не понимаю, что проблема в том (может быть, абстрактная машина C++ не может работать с памятью, представленной картофелем фри с перцем?)

xor256 25.11.2018 06:11

@ xor256 Я все еще думаю, что это слишком остро. Старые добрые классы хранения, к которым каждый из нас давно привык в эпоху C, точно так же работают и в C++. Следовательно, все, что касается указателей, работает точно так же. Единственная сложная часть - это создание / разрушение объекта, но размещение нового / удаления одного объекта дает возможность технически вызвать конструктор и деструктор как функцию. Конечно, вы можете выстрелить себе в ногу, если попытаетесь, но у нас все еще есть необходимый набор инструментов, и им можно надежно воспользоваться.

Géza Török 25.11.2018 09:40

@ xor256 Так что я тоже не очень понимаю, в чем проблема на самом деле ...

Géza Török 25.11.2018 17:47

@ GézaTörök: Проблема в том, что компиляторы предполагают, что если два указателя или lvalue «не могут» идентифицировать одно и то же хранилище, операции над ними могут быть безопасно переупорядочены относительно друг друга. Тот факт, что указатель формируется из другого указателя с использованием арифметики указателя, не всегда достаточен, чтобы убедить некоторые компиляторы в том, что они могут идентифицировать одно и то же хранилище, если они думают, что правила типизации объектов запрещают указателям доступ к одному и тому же хранилищу.

supercat 07.03.2020 23:18

@supercat Я думаю, вы говорите об очень редких случаях для версий компилятора до введения строгого алиасинга. Если бы такое неверное толкование было обычным явлением, это, например, сделало бы reinterpret_cast совершенно бесполезным.

Géza Török 10.03.2020 16:58

@ GézaTörök: Напротив, проблема не в предустановленных версиях компилятора, а в компиляторах, которые - вместо того, чтобы интерпретировать правила «строгого псевдонима», просто говоря, что компиляторы могут предположить, что указатели на первый взгляд несвязанный не имеют псевдонимов - вместо этого интерпретируйте правила как приглашение игнорировать очевидные отношения между указателями. Стандарт не указывает, что для данного float *p; компилятор должен рассматривать *(uint32_t*)p += 0x08000000; как потенциальный доступ к любому объекту типа float, который может быть идентифицирован p, потому что авторы Стандарта ...

supercat 10.03.2020 17:13

... считал очевидным, что такие конструкции должны обрабатываться «документированным образом, характерным для окружающей среды» в ситуациях, когда это было бы полезно и практично, независимо от того, требуется ли это в стандарте или нет. Вопрос о том, когда поддерживать такие конструкции, был оставлен как проблема качества реализации, о чем, по мнению авторов Стандарта, рынок мог судить лучше, чем Комитет. Что касается компиляторов, за которые люди действительно будут платить, я думаю, они были правы, но объединение gcc с Linux защитило его от рыночных сил.

supercat 10.03.2020 17:19
Ответ принят как подходящий

Стандарты C++ содержат открытый проблема, что базовое представление объектов не является «массивом», а «последовательностью» объектов unsigned char. Тем не менее, все рассматривают его как массив (что и задумано), поэтому можно безопасно писать такой код:

char* storage = static_cast<char*>(operator new(sizeof(T)*size));
// ...
char* p = storage + sizeof(T)*i;  // precondition: 0 <= i < size
new (p) T(element);

пока void* operator new(size_t) возвращает правильно выровненное значение. Использование смещений, умноженных на sizeof, для сохранения выравнивания - безопасно.

В C++ 17 есть макрос STDCPP_DEFAULT_NEW_ALIGNMENT, который определяет максимально безопасное выравнивание для «нормального» void* operator new(size_t), и void* operator new(std::size_t size, std::align_val_t alignment) следует использовать, если требуется большее выравнивание.

В более ранних версиях C++ такого различия нет, а это означает, что void* operator new(size_t) должен быть реализован таким образом, чтобы он был совместим с выравниванием любого объекта.

Что касается возможности выполнять арифметические операции с указателями непосредственно на T*, я не уверен, что стандарт требует наличия потребности. Однако сложно реализовать модель памяти C++ так, чтобы она не работала.

CWG 1701 не имеет ничего общего с рассматриваемой проблемой. CWG 1701 посвящена представлению объектов. Проблема с распределением функций в том, что они не создают объекты. Как здесь должно помочь решение вопроса?

Language Lawyer 10.01.2019 10:28
все рассматривают его как массив (что и задумано) Откуда это "то, что предназначено"? Примечание из выпуска: Был поднят дополнительный вопрос о том, уместно ли ссылаться на составляющие байты объекта как на сами «объекты».. Как это должно быть массивом, когда его элементы не должны быть объектами?
Language Lawyer 10.01.2019 10:33

@LanguageLawyer, нет правда, что функции распределения не создают объекты. См. стандарт. Предназначен авторами языка, что следует из того, как они (и все остальные) используют такие конструкции; если сомневаетесь, вы можете спросить их напрямую, их электронные письма не являются секретными.

Kit. 10.01.2019 18:52

@Набор. См. Стандарт. Он не говорит, что объект создан, он говорит, что его время жизни началось. См. Стандарт, когда объект создается.

Language Lawyer 10.01.2019 21:17

@LanguageLawyer, то «когда объект создается» - соломинка. Семантически нет никакой разницы, «создает» ли функция распределения или «ссылается» на массив байтов.

Kit. 10.01.2019 21:33

@Набор. Нет, это не соломинка. Это предназначено. Ваше (очень популярное среди людей) неправильное толкование правила начала срока службы, согласно которому мириады объектов волшебным образом появляются в хранилище соответствующего размера и выравнивания, явно противоречит нескольким правилам, например, когда объекты в течение своего времени жизни могут иметь один и тот же адрес, что показывает, что такие толкование не предназначалось Комитетом.

Language Lawyer 10.01.2019 21:45

@LanguageLawyer, нет, это ваша интерпретация того, что они появляются «волшебным образом». В стандарте четко указано, что их срок службы начинается с момента получения хранилища с правильным выравниванием и размером. Если вы считаете, что это противоречит чему-то еще в стандарте, отправьте отчет о дефекте.

Kit. 11.01.2019 00:39

@Набор. В стандарте четко указано, что их срок службы начинается с момента получения хранилища с правильным выравниванием и размером. Ага. Когда объект создается, первое, что для него получается хранилище. И если нет непустой инициализации, запускается время жизни создаваемого объекта. Это правильная интерпретация правила.

Language Lawyer 11.01.2019 07:59

@Набор. В любом случае, здесь - это предложение члена Комитета, говорящее «это поддерживает статус-кво, что одного malloc недостаточно для создания объекта». Вы сказали "неверно, что функции распределения не создают объекты. Предназначено авторами языка". Как видим, не предполагалось.

Language Lawyer 11.01.2019 08:02

@LanguageLawyer, C++ был создан и развивается как язык, одним из самых сильных аргументов которого является способность работать с объектами POD не создается языковыми конструкциями (от аппаратных регистров до данных в файлах, отображаемых в память, до объектов, созданных в том же процессе с помощью кода, написанного на другом языке). язык). Если бы Комитет однажды решил запретить эту способность, такое глупое решение вызвало бы массовый протест в отрасли, который невозможно было бы пропустить.

Kit. 12.01.2019 16:20

@Набор. Если бы объекты волшебным образом появились в любом подходящем хранилище, тогда с кодом в сообщении OP не было бы проблем, потому что там появился бы массив беззнаковых символов, охватывающий всю часть выделенного хранилища. Но это не так. См. Предложение в ответе с самым высоким рейтингом.

Language Lawyer 12.01.2019 16:31

@LanguageLawyer, код в сообщении OP не имеет проблем, за исключением тех, которые, возможно, относятся к STDCPP_DEFAULT_NEW_ALIGNMENT. Что имеет проблемы с этим кодом, так это интерпретация, что объекты в текущем C++ могут начать свое время жизни только в результате создания объекта языковые конструкции. Хотя изменение языка таким образом, чтобы эта интерпретация стала правильной, может показаться хорошей идеей, это может излишне нарушить большой объем существующего кода, особенно в автономных реализациях, не дав взамен ничего полезного.

Kit. 12.01.2019 17:41

Другие вопросы по теме