У меня есть небольшой вопрос о выводе типов в метапрограммировании C ++.
Есть определенная функция, выполняющая какое-то действие.
main.cpp
template<typename T> void foo(T arg) {
// do some action on argument
std::cout << typeid(arg).name() << std::endl;
}int main(int argc, char** argv) {
int array[100] = {0};
std::cout << typeid(array).name() << std::endl;
foo(array);
return 0;
}
Выход:
A100_i
Pi
Зачем Arg в функции Foo () иметь другой тип данных, чем массив в функции главный()?
Потому что массивы в стиле C сломаны. В частности, вы не можете
иметь аргумент функции с типом массива в стиле C; если ты пишешь
функция (забыв про шаблоны на данный момент):
void foo( int arg[100] );
язык требует, чтобы компилятор рассматривал это как:
void foo( int* arg );
(и 100
это просто комментарий — он игнорируется
компилятор).
Для поддержки этого в случае шаблонов, если
компилятор пытается сопоставить не ссылочный аргумент шаблона,
он преобразует аргумент массива в указатель и наберет
вывод приведет к типу указателя, а не к типу массива.
В результате вы никогда не должны писать функцию (шаблон
или иначе) ожидание массива стиля C (кроме второго
аргумент main
, где у вас нет выбора).
Так как эта поломка присутствует только по причинам
Совместимость с C, C ++ не следует этому, когда ссылки
участвует. Так:
template < typename T, size_t N >
void foo( T (&arg)[ N ] );
будет работать, и должен дать вам одинаковые результаты в обоих случаях.
Если вы думаете, что ваша функция может быть вызвана с
Массивы в стиле C и другие вещи (например, std :: vector), вы можете
перегрузить его для обоих. Версия выше более специализированная,
и будет предпочтительнее более общей версии, если это возможно.
Лучшим решением было бы полностью избежать массивов в стиле C, но
они полезны для статических переменных с инициализацией; его
только с массивами в стиле C, которые вы можете заставить рассчитывать компилятор
количество элементов и определить размер массива
согласно списку инициализатора. И иметь статический
инициализация; std::vector
будет считать инициализаторы в
время выполнения, но используется как статическая переменная, может вызвать порядок
проблемы инициализации.
Массивы в стиле C и
На самом деле, когда вы передаете массив функции, он распадается на тип указателя. Так T
выводитсяint*
, вместо int[100]
,
Если вы хотите предотвратить затухание, примите параметр по ссылке.:
template<typename T> void foo(T & arg) //Note `&` here!
{
// do some action on argument
std::cout << (typeid(arg).name() << std::endl;
}
Теперь он напечатает то, что вы ожидаете, т.е. A100_i
, Увидеть это онлайн демо.
Вопрос: почему массив переходит в тип указателя, когда мы передаем по значению?
Ответ: потому что в C ++ массивы (и функции) не могу пройти по значению. Язык не позволяет этого. Вместо этого язык требует, чтобы они превратились в указатель, когда они передаются в качестве аргументов функции. Чтобы предотвратить распад, мы должны передать их как ссылка.