Распад массива на указатели в шаблонах - PullRequest
6 голосов
/ 08 декабря 2009

Пожалуйста, примите во внимание этот код:

#include <iostream>

template<typename T>
void f(T x) {
    std::cout << sizeof(T) << '\n';
}

int main()
{
    int array[27];
    f(array);
    f<decltype(array)>(array);
}

Примечание редактора: использовался оригинальный код typeof(array), однако это расширение GCC.

Это напечатает

8 (or 4)
108

В первом случае массив явно распадается на указатель, и T становится int*. Во втором случае T принудительно устанавливается на int[27]. Определен ли порядок реализации распада / замещения? Есть ли более элегантный способ заставить тип к int[27]? Помимо использования std :: vector?

Ответы [ 4 ]

9 голосов
/ 08 декабря 2009

Используйте тип ссылки для параметра

template<typename T> void f(const T& x) 
{
  std::cout << sizeof(T);
}

в этом случае тип массива не будет затухать.

Аналогично, вы также можете предотвратить затухание в исходной версии f, если явно указали шаблон T в качестве типа ссылки на массив

f<int (&)[27]>(array);

В исходном примере кода принудительное использование аргумента T для типа массива (т. Е. Не ссылочного типа массива с помощью typeof или путем явного указания типа) не предотвратит затухание типа массива. Хотя T сам по себе будет обозначать тип массива (как вы заметили), параметр x все равно будет объявлен как указатель, а sizeof x будет по-прежнему иметь размер указателя.

2 голосов
/ 16 ноября 2015

Поведение этого кода объясняется C ++ 14 [temp.deduct.call]:

Вывод аргументов шаблона из вызова функции

Вывод аргумента шаблона выполняется путем сравнения каждого типа параметра шаблона функции (назовите его P) с типом соответствующего аргумента вызова (назовите его A), как описано ниже

, а затем ниже:

Если P не является ссылочным типом:

  • Если A является типом массива, тип указателя, созданный стандартным преобразованием массива в указатель (4.2), используется вместо A для вывода типа;

Для звонка f(array); у нас есть A = int[27]. A - это тип массива. Таким образом, выведенный тип T равен int *, в соответствии с этой последней точкой.

Из квалификатора «Если P не является ссылочным типом» мы можем видеть, что такого поведения можно было бы избежать, если сделать P ссылочным типом. Для кода:

template<typename T, size_t N>
void f(T (&x)[N])

символ P означает T(&)[N], который является ссылочным типом; и оказывается, что здесь нет примененных преобразований. T выводится на int, с типом x, равным int(&)[N].


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

1 голос
/ 08 декабря 2009

Вы также можете использовать шаблоны, подобные следующим:

template <typename T, std::size_t N>
inline std::size_t number_of_elements(T (&ary)[N]) {
    return N;
}

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

1 голос
/ 08 декабря 2009

В зависимости от вашего варианта использования вы можете обойти это , используя ссылки :

template<typename T>
void f(const T& x) {
    std::cout << sizeof(T);
}

char a[27];
f(a);

Это печатает 27, по желанию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...