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

#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++ использовать new-expression с массивом нулевой длины, следующим за вторым синтаксическим путем, то есть с идентификатором_типа и круглые скобки (случай 2)?

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

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

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

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

Примечания:

  1. ISO/IEC 14882:2003, раздел 8.3.4, параграф 1:
    #P9#
  2. ISO/IEC 14882:2003, раздел 5.3.4, параграф 6:
    #P10#
  3. ISO/IEC 14882:2003, раздел 5.3.4, параграф 7:
    #P11#
  4. ISO/IEC 14882:2003, раздел 5.3.4, параграф 1:
    #P12# #P13# #P14#
  5. Хотя приведенные выше цитаты взяты из стандарта С++ 03, насколько мне известно, эта проблема все еще неясна в более новых версиях стандарта С++ (С++ 11, С++ 14 и С++ 17).
  6. Интересный пост Херба Саттера о нуле -длинные массивы.
  7. Код в примере представляет собой слегка измененный тест из пакета SuperTest SolidSands.

person José Luis    schedule 30.04.2018    source источник
comment
Просто любопытно: есть ли причина, по которой вы цитируете стандарт С++ 03, если хотите соответствия С++ 11? Поскольку в Интернете доступны различные черновики, которые может быть легче цитировать/ссылать.   -  person Rakete1111    schedule 30.04.2018
comment
Что с кастомным багги operator new[]? Какое это имеет отношение к чему-либо?   -  person Cheers and hth. - Alf    schedule 30.04.2018
comment
Никаких предупреждений от gcc 8.   -  person Sam Varshavchik    schedule 30.04.2018
comment
@ Rakete1111Меня интересует соответствие требованиям, начиная с C++03. Но вы правы, было бы лучше просто дать ссылку на Стандарт, а не цитировать напрямую. Если я найду онлайн-ссылку на раздел (не PDF) стандарта С++ 03, я обновлю сообщение.   -  person José Luis    schedule 01.05.2018
comment
@Cheersandhth.-Alf Я думаю, что проблема будет такой же, я оставил ее, потому что не хотел сильно изменять исходный тест (я уже уменьшил его в других частях). Почему глючит?   -  person José Luis    schedule 01.05.2018
comment
@JoséLuis: он не гарантирует возврата хотя бы запрошенного количества байтов, если он вернется.   -  person Cheers and hth. - Alf    schedule 01.05.2018


Ответы (2)


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

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

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

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


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

int x[42];

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

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

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

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

person Cheers and hth. - Alf    schedule 01.05.2018

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

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

person Davis Herring    schedule 01.05.2018
comment
Также динамически выделяемый массив является объектом. - person Cheers and hth. - Alf; 01.05.2018
comment
@Cheersandhth.-Alf: Да, но в /7 у нас есть уникальная идея, что можно «выделить массив без элементов». - person Davis Herring; 01.05.2018
comment
Я хотел сказать, что утверждение «new с типом в скобках пытается создать объект этого типа» бессмысленно, потому что каждое (успешное) вычисление выражения new создает объект. - person Cheers and hth. - Alf; 01.05.2018
comment
@Cheersandhth.-Alf: Какой объект создает new int[0]? Единственный разумный тип для него нельзя назвать (даже через decltype). Возможно, в любом случае можно сказать, что он существует, но это ставит под сомнение «new всегда создает объект». - person Davis Herring; 01.05.2018
comment
Это int[], это максимум, что известно. То же самое относится и к объекту, созданному new int[argc]. - person Cheers and hth. - Alf; 01.05.2018
comment
@Cheersandhth.-Alf: не создается объект с незавершенным (на тот момент) типом. - person Davis Herring; 02.05.2018
comment
Когда вы отрицаете, что тип объекта массива, созданного new int[argc], является неполным, вы должны откашливать полный тип, который, по вашему мнению, он имеет. Или же, поскольку каждый объект C++ имеет тип, вы отрицаете, что объект вообще существует. Но указать полный тип невозможно, так как типы должны быть известны во время компиляции, а значение argc неизвестно до времени выполнения. Так что то, что вы утверждаете, невозможно. Другими словами, эта цепочка отрицания ведет прямо в Территорию Заблуждений. КЭД - person Cheers and hth. - Alf; 02.05.2018