Должен ли поставщик хранилища иметь тип char, unsigned char или std::byte? или это может быть любой тип?

это четко определено или UB?

alignas(alingof(T)) char buffer[sizeof(T)];

auto* p = new(buffer) T;
    
// use p
p->~T(); // destroy at end

буфер должен иметь тип char, unsigned char или std::byte? или это может быть любой тип, например int?

alignas(alignof(T)) int buffer[sizeof(T)/(sizeof(*buffer) + ((sizeof(T)%sizeof(*buffer)!=0)];

Вам следует писать alignas(T), а не alignas(alignof(T)). Более того, во втором примере вы, скорее всего, допустили опечатку, поменяв местами alignof() и alignas().

LoS 19.06.2024 21:15

пока память имеет правильное выравнивание и достаточный размер для объекта, какой фактический объект хранения не имеет значения.

NathanOliver 19.06.2024 22:06

Преимущество буфера byte в том, что он может соответствовать точному размеру. Как вы заметили, иметь 4,5 int — это довольно утомительно, и в нем легко ошибиться.

BoP 19.06.2024 22:29
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
102
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это не обязательно должен быть какой-то конкретный тип.

Однако формально существует особый случай, если тип элемента массива — unsigned char или std::byte. В этом случае создание объекта T не приведет к прекращению существования объекта массива. Вместо этого объект массива будет обеспечивать хранилище для нового объекта T, и новый объект T будет вложен в объект массива.

Для любого другого типа элемента, включая char, создание объекта T в его хранилище приведет к прекращению существования массива. Это не напрямую касается вашего использования.

Однако если тип элемента имеет нетривиальный деструктор, то прекращение существования элементов массива становится проблемой, когда вы достигаете точки, где обычно деструктор вызывается неявно. Если вы не восстановите эквивалентные новые объекты для каждого элемента массива, чтобы они находились в пределах своего существования на момент достижения этой точки, то поведение программы будет неопределенным согласно [basic.life]/10.

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

Кроме того, тип элемента не должен быть const-квалифицированным. В противном случае попытка повторного использования хранилища будет иметь неопределенное поведение согласно [basic.life]/11.

И технически вам следует писать ::new(static_cast<void*>(buffer)) T или std::construct_at(reinterpret_cast<T*>(buffer)) вместо new(buffer) T. Иначе вы можете случайно вызвать не туда operator new.

Очевидным преимуществом char, unsigned char или std::byte является то, что расчет размера намного проще, поскольку их размеры гарантированно будут 1. И std::byte имеет дополнительное преимущество, поскольку дает понять, что массив предназначен либо для хранения необработанных байтов информации, либо для хранения другого объекта. char и unsigned char обычно являются арифметическими типами.

В любом случае попытка чтения/записи элементов массива после нового размещения будет UB. Это не способ обойти правила псевдонимов типов. Доступ через указатель, возвращаемый new, обычно будет безопасным.

поэтому чтение массива беззнаковых символов и стандартных байтов разрешено после размещения нового? а как насчет cwg2489?

user24551355 19.06.2024 23:21

@ user24551355 В настоящее время это не так, но, вероятно, это разрешено. Вероятно, это должно привести к чтению объектного представления вложенного объекта, то есть так же, как если бы вы получили доступ через reinterpret_cast<unsigned char*>(p). Это также в настоящее время указано неправильно.

user17732522 19.06.2024 23:23

@user24551355 CWG2489 удален char из списка, который может предоставить хранилище, как это также отражено в моем ответе. Тем не менее, для того, что вы пытаетесь сделать, вам не нужен массив для хранения данных. Вас просто интересует повторное использование хранилища массива.

user17732522 19.06.2024 23:24

@user117232522 user117232522 о, ладно, я думаю, что хранилище любого типа можно использовать повторно, но оно не может «обеспечить», Боже, стандарт трудно читать и понимать, есть ли у вас какие-нибудь советы, как его читать?

user24551355 19.06.2024 23:29

Я думаю, что reinterpret_cast int в беззнаковый или стандартный байт разрешен, думаю, им разрешено использовать псевдоним

user24551355 19.06.2024 23:30

@ user24551355 Определения фраз, выделенных курсивом, следует рассматривать как определения в математических теоремах. Даже если «повторное использование хранилища» и «предоставление хранилища» звучат одинаково, не делайте никаких неявных предположений об их взаимосвязи. Просто посмотрите, где в стандарте используется эта же фраза. Это единственные контексты, для которых это имеет значение. К сожалению, у объектной системы все еще есть несколько проблем со спецификациями, за которыми тоже нелегко следить.

user17732522 19.06.2024 23:36

@user24551355 Если я где-то не понимаю намерения, я пытаюсь поискать github.com/cplusplus/draft/issues , open-std.org/jtc1/sc22/wg21/docs/cwg_active.html , ️ 🔁 lists.isocpp.org/mailman/listinfo.cgi , github.com/cplusplus/papers/issues и здесь для обсуждения соответствующей темы. Если повезет, можно найти прямые комментарии/ответы от членов комитета.

user17732522 19.06.2024 23:36

@user24551355 user24551355 Да, как я уже сказал, намерение явно состоит в том, чтобы доступ через unsigned char и std::byte был разрешен, поэтому они исключены из правила псевдонимов. Однако семантика этого в настоящее время определена неправильно. См., например. github.com/cplusplus/papers/issues/592 и github.com/cplusplus/CWG/issues/548 . А также, конкретно обсуждая случай массивов, обеспечивающих хранилище, см. github.com/cplusplus/CWG/issues/416.

user17732522 19.06.2024 23:40

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