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

StackOverflow https://stackoverflow.com/questions/472530

Вопрос

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

#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 типом размера, с предполагаемым std::size_t типом размера, но не с выводимым std::ptrdiff_t типом размера?

РЕДАКТИРОВАТЬ Серж хочет знать, где мне понадобится первая версия. Во-первых, этот пример кода явно упрощен. Мой настоящий код немного сложнее. Массив на самом деле является массивом индексов / смещений в другом массиве. Таким образом, по логике тип массива должен совпадать с типом размера для максимальной корректности. В противном случае я могу получить несоответствие типов (например, между <=> и <=>). По общему признанию, это не должно быть проблемой на практике, так как компилятор неявно преобразует в больший из двух типов.

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

Это было полезно?

Решение

Хм, стандарт говорит в 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), делают это неправильно. Я протестировал код с Comeau, и он прекрасно компилирует ваш код. Я не думаю, что есть разница в том, зависит ли тип параметра шаблона нетипичного типа от параметра типа или нет. 14.8.2.4/2 говорит, что аргументы шаблона должны быть выведены независимо друг от друга, а затем объединены в тип функции-параметра. В сочетании с / 15, который позволяет типу измерения быть другого целочисленного типа, я думаю, что ваш код в порядке. Как всегда, я беру карту C ++ - это сложно, поэтому я могу ошибаться :)

Обновление . Я изучил отрывок в 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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top