Z-массив с нулевой длиной связывается с типом указателя

Недавно я написал шаблон функции, который ссылается на C-массив:

template <class T, size_t N>
void foo(T(&c_array)[N]);

Предполагая, что T является char, длина C-строки равна N - 1 из-за нулевого терминатора. Я понял, что я должен, вероятно, обработать краевой регистр, где N == 0, потому что тогда N - 1 будет std::numeric_limits<std::size_t>::max().

Итак, чтобы избежать хаоса, который может возникнуть в редком случае, когда кто-то передает массив нулевой длины этой функции, я поставил чек на N == 0.

Однако, к моему удивлению, кажется, что массив нулевой длины фактически не является даже типом массива - или, по крайней мере, тем, что, по-видимому, считает GCC. Фактически, массив нулевой длины даже не связывается с указанной сигнатурой функции, если в качестве кандидата доступна функция с сигнатурой типа указателя.

Рассмотрим следующий код:

template <class T, size_t N>
void foo(T(&array)[N])
{
    std::cout << "Array" << std::endl;
}

void foo(const void* p)
{
    std::cout << "Pointer" << std::endl;
}

int main(int argc, char** argv)
{
    char array1[10] = { };
    const char* pointer = 0;
    char array2[0] = { };

    foo(array1);
    foo(pointer);
    foo(array2);
}

С GCC 4.3.2 это выдает:

Array
Pointer
Pointer

Как ни странно, массив нулевой длины предпочитает связываться с функцией, которая принимает тип указателя. Итак, является ли это ошибкой в ​​GCC или существует какая-то неясная причина, установленная стандартом С++, почему это поведение необходимо?

Ответ 1

Поскольку массивы должны иметь длину больше нуля, если ваш компилятор ошибочно принимает определение массива нулевого размера, то вы "безопасно" за пределами уровня языка. Вам не нужно обрабатывать граничный случай N == 0.

Это верно в С++: 8.3.5 [dcl.array]: Если присутствует константное выражение (5.19), оно должно быть интегральным постоянным выражением и его значение должно быть больше нуля.

Ответ 2

По-видимому, ISO C запрещает массивы длиной 0 длины, что, вероятно, влияет на то, как GCC пытается скомпилировать материал. См. Этот вопрос для получения дополнительной информации! массивы нулевой длины против указателей

Ответ 3

Руководство GCC имеет целую вещь на массивах нулевой длины. Это расширение GCC, что несколько похоже на неполные массивы.

Ответ 4

Говоря о C (и, возможно, также С++ в этом случае), определение массива нулевой длины - это поведение undefined, поэтому GCC, вероятно, делает это, потому что a) ничего не останавливает его, и b) он предотвращает ошибки, подобные тем, которые вы 'старайтесь избегать.

Ответ 5

В С++ нет массивов нулевой длины. Однако, если это так, вот как вы могли бы справиться с этим случаем:

template <bool B, typename T>
struct disable_if;

template <typename T>
struct disable_if<false, T>
{
    typedef T type;
};

template <class T, size_t N>
typename disable_if<N == 0, void>::type foo(T(&c_array)[N])
{
    std::cout << "inside template\n";
}

Ответ 6

Массив нулевого размера является только законным в C как последний элемент atruct. Все остальное бессмысленно.