Подтвердить что ты не робот

Как передать размер массива в виде шаблона с типом шаблона?

Мой компилятор ведет себя странно, когда я пытаюсь передать массив фиксированного размера в функцию шаблона. Код выглядит следующим образом:

#include <algorithm>
#include <iostream>
#include <iterator>

template <typename TSize, TSize N>
void f(TSize (& array)[N]) {
    std::copy(array, array + N, std::ostream_iterator<TSize>(std::cout, " "));
    std::cout << std::endl;
}

int main() {
    int x[] = { 1, 2, 3, 4, 5 };
    unsigned int y[] = { 1, 2, 3, 4, 5 };
    f(x);
    f(y); //line 15 (see the error message)
}

Он производит следующую компиляцию в GCC 4.1.2:

test.cpp|15| error: size of array has non-integral type ‘TSize’
test.cpp|15| error: invalid initialization of reference of type
  ‘unsigned int (&)[1]’ from expression of type ‘unsigned int [5]’
test.cpp|6| error: in passing argument 1 of ‘void f(TSize (&)[N])
  [with TSize = unsigned int, TSize N = ((TSize)5)]’

Обратите внимание, что первый вызов компилируется и выполняется успешно. Это, по-видимому, означает, что хотя int является интегралом, unsigned int не является.

Однако, если я изменю объявление моего вышеописанного шаблона функции на

template <typename TSize, unsigned int N>
void f(TSize (& array)[N])

проблема просто уходит! Обратите внимание, что единственное изменение здесь - от TSize N до unsigned int N.

Раздел [dcl.type.simple] в окончательном проекте ISO/IEC FDIS 14882: 1998, по-видимому, подразумевает, что "интегральный тип" либо подписан, либо без знака:

Спецификатор signed заставляет char объекты и битовые поля подписать; он избыточен с другими интегральными типами.

Что касается объявлений массивов фиксированного размера, проект говорит [ dcl.array]:

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

Итак, почему мой код работает с явным типом размера unsigned с типом inferred signed, но не с типом типа unsigned?

EDIT Серж хочет знать, где мне понадобится первая версия. Во-первых, этот пример кода, очевидно, упрощен. Мой реальный код немного более сложный. Массив на самом деле представляет собой массив индексов/смещений в другом массиве. Итак, логически, тип массива должен быть таким же, как и его тип размера, для максимальной корректности. В противном случае я могу получить несоответствие типа (например, между unsigned int и std::size_t). По общему признанию, это не должно быть проблемой на практике, поскольку компилятор неявно преобразуется в больший из двух типов.

РЕДАКТИРОВАТЬ 2 Я стою исправлено (спасибо, litb): размер и смещение, конечно, логически разные типы, а смещения в массивах C, в частности, имеют тип std::ptrdiff_t.

4b9b3361

Ответ 1

Хмм, стандарт говорит в 14.8.2.4 / 15:

Если в объявлении шаблона функции с несимвольным шаблоном-параметром параметр-шаблон непигового типа используется в выражении в списке параметров функции и, если выведен соответствующий шаблон-аргумент, тип шаблона-шаблона должен точно соответствовать типу шаблона-параметра, за исключением того, что аргумент-шаблон, выведенный из привязки массива, может иметь любой интегральный тип.

Предоставление этого примера:

template<int i> class A { /* ... */ };
template<short s> void f(A<s>);
void k1() {
    A<1> a;
    f(a);    // error: deduction fails for conversion from int to short
    f<1>(a); // OK
}

Это говорит о том, что компиляторы, которые не могут скомпилировать ваш код (очевидно, GCC и Digital Mars), делают это неправильно. Я тестировал код с помощью Комо, и он компилирует ваш код в порядке. Я не думаю, что существует другое отличие от того, зависит ли тип параметра непигового шаблона от типа параметра типа или нет. 14.8.2.4/2 говорит, что аргументы шаблона должны быть выведены независимо друг от друга, а затем объединены в тип функционального параметра. В сочетании с /15, который позволяет типу измерения иметь разный интегральный тип, я думаю, что ваш код в порядке. Как всегда, я беру карту С++ - is-complex-so-i-may-be-wrong:)

Обновление. Я просмотрел прохождение в GCC, где он выплевывает это сообщение об ошибке:

  ...
  type = TREE_TYPE (size);
  /* The array bound must be an integer type.  */
  if (!dependent_type_p (type) && !INTEGRAL_TYPE_P (type))
    {
      if (name)
    error ("size of array %qD has non-integral type %qT", name, type);
      else
    error ("size of array has non-integral type %qT", type);
      size = integer_one_node;
      type = TREE_TYPE (size);
    }
  ...

Кажется, что пропустили, чтобы отметить тип размера как зависимый в более раннем блоке кода. Поскольку этот тип является параметром шаблона, он является зависимым типом (см. 14.6.2.1).

Обновление: Разработчики GCC исправили его: Ошибка # 38950