Динамические массивы нулевой длины в С++

#include <stdlib.h>

void *operator new[](size_t size, int n){
    if( size != 0 && n != 0 )
        return calloc(n, size);
    return calloc(1, 1);
}

int main(){

    int * p1;
    const int i = 0;

//  p1 = new (20)  int[i] ; // Case 1 (OK)
    p1 = new (20) (int[i]); // Case 2 (Warning)

    if( p1 == 0 )
        return 1;
    return 0;
}

Этот код (https://godbolt.org/g/hjo7Xn) успешно компилируется с Clang 6.0.0, однако GCC 7.3 выдает предупреждение о том, что массивы нулевой длины запрещены в C++. Если скобки удалены (случай 1), предупреждение исчезает.

В отличие от статически распределенных массивов нулевой длины (C++ 03: 8.3.4/1) допускаются динамически распределенные массивы нулевой длины (C++ 03: 5.3.4/6). Тем не менее, в стандарте C++ последние явно разрешены только при выполнении одного из двух возможных путей синтаксиса нового выражения, то есть с идентификатором нового типа и без круглых скобок (случай 1).

Разрешено ли стандартом C++ использовать новое выражение с массивом нулевой длины после второго пути синтаксиса, то есть с идентификатором типа и круглыми скобками (случай 2)?

Единственная связанная цитата - C++ 03: 5.3.4/5:

Когда выделенным объектом является массив (т.е. Используется синтаксис direct-new-declarator или идентификатор нового типа или идентификатор типа обозначает тип массива), новое выражение дает указатель на исходный элемент (если любой) массива.

Однако формулировка (if any) допускает массив без элементов, однако не ясно, относится ли оно к обоим случаям или относится только к идентификатору нового типа и без круглых скобок (случай 1).

Заранее спасибо.

Заметки:

  1. ISO/IEC 14882: 2003, раздел 8.3.4, пункт 1:

    Если присутствует постоянное выражение (5.19), оно должно быть интегральным постоянным выражением, и его значение должно быть больше нуля.

  2. ISO/IEC 14882: 2003, раздел 5.3.4, абзац 6:

    Выражение в direct-new-declarator должно иметь интегральный или перечисляемый тип (3.9.1) с неотрицательным значением.

  3. ISO/IEC 14882: 2003, раздел 5.3.4, пункт 7:

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

  4. ISO/IEC 14882: 2003, раздел 5.3.4, пункт 1:

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

    :: opt new new-placement opt new-type-id new-initializer opt

    :: opt new new-placement opt (type-id) new-initializer opt

  5. Хотя приведенные выше цитаты из стандарта C++ 03, насколько мне известно, эта проблема все еще неясно в новых версиях стандарта C++ (C++ 11, C++ 14 и C++ 17).
  6. Интересный Herb Sutter сообщение о нулевой длины массивов.
  7. Код в этом примере представляет собой слегка измененный тест из пакета SuperTest от SolidSands.

Ответ 1

В скобках у вас есть обычный идентификатор типа, а не специальный синтаксис, называемый идентификатором нового типа, который поддерживает размер динамического массива.

Начиная с С++ 17 в стандарте нет специального положения для этого использования идентификатора типа, поэтому вопрос сводится к тому, можете ли вы писать

auto main() -> int
{
    using Argh = int[0];
}

Вы не можете, потому что тип типа-типа определяется в терминах фиктивного "объявления для переменной или функции этого типа, который опускает имя объекта" (С++ 17 §11.1/1), и для объявления переменной массива правило: "Если присутствует константное выражение (8.20), оно должно быть преобразованным константным выражением типа std::size_t а его значение должно быть больше нуля" (С++ 17 §11.3.4/1).


Теперь в этом есть справедливая интерпретация. Например, без такой разумной интерпретации последняя цитата не будет разумно сказать, что размер массива должен быть неотрицательным и быть представленным как size_t. Вместо этого, без разумного толкования, оно буквально скажет, что декларация, такая как

int x[42];

(конечно, это не так) и должно быть выражено как

int x[std::size_t(42)];

Определение того, что является разумной интерпретацией или не используется, чтобы быть легким. Можно просто спросить, имеет ли это смысл? Таким образом, для случая выше ответ будет отрицательным, и можно было бы отказаться от этой возможности.

Однако, в некоторой степени с С++ 14 и все более и более с С++ 17, я нахожу, что предыдущий надежный метод терпит неудачу. Однако, поскольку вопрос о функциональности С++ 03, я думаю, вы можете доверять этому ответу. Но если бы речь шла о С++ 14 или более поздних материалах, то имейте в виду, что любой явно четкий ответ, вероятно, включает в себя некоторые субъективные интерпретации, которые, вероятно, не могут быть решены, если спросить, имеет ли это смысл или нет.

Ответ 2

Нет, случай с нулевым размером не может использовать идентификатор типа в скобках. Поведение для массива размера 0 задается (в текущем черновике) только для выражения в noptr-new-declarator ([expr.new]/7). new с типом в скобках пытается создать объект такого типа, и нет массивов размера 0 ([dcl.array]/1), даже не как идентификатор типа ([dcl.name]/1).

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