Новое размещение в элементах массива и значениях указателя - PullRequest
4 голосов
/ 07 апреля 2020

Рассмотрим следующие три программы:

// program 1

#include<new>

struct A {
    const int a = 0;
    int b = 0;
};

int main() {
    auto a = new A[2];
    new(a+1) A;
    a[1].b = 1;
}
// program 2

#include<new>

struct A {
    const int a = 0;
    int b = 0;
};

int main() {
    auto a = new A[2];
    new(a) A;
    a[0].b = 1;
}
// program 3

#include<new>

struct A {
    const int a = 0;
    int b = 0;
};

int main() {
    auto a = new A[2];
    new(a) A;
    a->b = 1;
}

Есть ли у этих программ неопределенное поведение в C ++ 17?

Я вижу проблему в том, что согласно [basi c .life] / 8 указатели не будут автоматически ссылаться на новые A объекты, которые я создаю с помощью размещения нового. std::launder требуется для достижения этой цели. Таким образом, доступ к элементу будет иметь неопределенное поведение, как и в случае объекта вне его времени жизни.

По крайней мере для программы 3 это кажется мне понятным, но для программы 1, согласно [intro.object ] / 2 вновь созданный объект становится подобъектом массива, и поэтому к нему следует обращаться через арифметику указателя c, не так ли?

И тогда программа 2 также не будет иметь неопределенного поведения потому что арифметика указателя c требует только a, чтобы указывать на элемент массива, а не обязательно на его время жизни.

1 Ответ

1 голос
/ 07 апреля 2020

Я считаю, что [basi c .life] /8.3

тип исходного объекта не является константным, и, если тип класса, не содержит какого-либо элемента данных non-stati c, тип которого является константным или ссылочным типом, и

является единственным предложением, которое делает ваш код доступа члена неопределенным поведением.

Во всех трех случаях a - это имя массива из двух A объектов, поэтому доступ к члену в порядке, потому что вы используете указатель A для доступа к A объекту. Вы не рассматриваете буфер символов как A, вы рассматриваете A как и A, что разрешено.

...