будет ли указатель, возвращаемый новым (size, value) Type [0], допустимым и может ли он использоваться для построения массива? - PullRequest
1 голос
/ 06 апреля 2011

Стандарт гласит: 5.3.4[expr.new]/7

Когда значение выражения в direct-new-таком объявителе равно нулю, функция выделения вызывается для выделения массива без элементов.

и в 3.7.3.1[basic.stc.dynamic.allocation]/2

Эффект разыменования указателя, возвращаемого как запрос нулевого размера, не определен.

Но еслиФункция выделения определяется пользователем, и она знает, что вернула действительный указатель. Будет ли все еще неопределенным поведение разыменовывать его?Может ли стандарт предписывать неопределенное поведение пользовательского кода?

Причиной, по которой я спрашиваю, является еще одна бессмысленная попытка инициализировать динамический массив объектов неконструктивного типа по умолчанию.Какие у него проблемы, кроме очевидного отсутствия delete[] и того, что его можно вызвать только с [0]?Я даже правильно использовал aligned_storage?

#include <type_traits>
#include <stdexcept>
#include <memory>
#include <iostream>

struct T {
   int val;
   T() = delete;
   T(int i) : val(i) {}
   void* operator new[](std::size_t, std::size_t cnt, const T& t)
   {
       typedef std::aligned_storage<sizeof(t),
                    std::alignment_of<T>::value>::type buf;
       T* ptr = reinterpret_cast<T*>(new buf[cnt]);
       std::uninitialized_fill_n(ptr, cnt, t);
       return ptr;
    }
};

int main()
{
    T* a = new(100, T(7)) T[0]; // using zero is legal per 5.3.4/7

    std::cout << "a[0] = " << a[0].val << '\n' // but is this legal?
              << "a[1] = " << a[1].val << '\n'
              << "a[98] = " << a[98].val << '\n'
              << "a[99] = " << a[99].val << '\n';
    delete[] a; // free the 100 aligned_storages
}

тестовый прогон: http://ideone.com/iBW0z

также компилируется и работает, как ожидается, с MSVC ++ 2010 EE

Ответы [ 2 ]

3 голосов
/ 06 апреля 2011

В вашем коде раздражающая логическая проблема:

Новое выражение:

T* a = new(100, T(7)) T[0];

Вызывает удаленный конструктор T по умолчанию [expr.new] / 17.; - (

std::vector<T> сейчас выглядит неплохо ...: -)

1 голос
/ 06 апреля 2011

Единственное неопределенное поведение использования результата reinterpret_cast - это когда приведение вернулось к его первоначальному типу, поэтому у вас уже есть UB, даже если все остальное было в порядке.

Если вам это действительно нужно, почему бы вам просто не сделать функцию, которая выделяет достаточно большой блок непрерывной памяти, а затем поместить new кучу T в эту память?

...