2013-02-26 5 views
0

У меня есть небольшой вопрос о дедукции типа в метапрограммировании 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() имеют тип данных, чем другой массив в функции главной() ?

+0

Я бы сказал: неправильное ожидание. возможно, хорошие ключевые слова google: распад указателя массива. – PlasmaHH

+0

Сотни вопросов были отправлены в SO на основе недоразумений из-за разложения матриц на указатели, обычно с участием 'sizeof', но это новый подход. :-) –

+0

Что вы хотите сказать, добавив ЗАКРЫТЬ к названию? –

ответ

3

Поскольку массивы типов 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 стиль массивы и

+1

+1 для подробного ответа. – Nawaz

6

Фактически, когда вы передаете массив функции, он распадается на тип указателя. Таким образом, 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. См. this online demo.


Вопрос: Почему гниение массив типа указателя, когда мы переходим по значению?

Ответ: Так как в массивах C++ (и функции) не могут быть переданыпо значению. Язык этого не позволяет. Вместо этого язык требует, чтобы они распадались на тип указателя, когда они передаются как аргументы функции. Чтобы предотвратить распад, нам необходимо передать их как ссылка.

+0

Еще более важным моментом, возможно: когда вы определяете аргумент, чтобы иметь тип массива, он автоматически преобразуется в определение указателя. –

+0

@JamesKanze: О, это определенно лучше и точнее. Я надеюсь, что OP прочтет этот комментарий, а также ваш ответ. :-) – Nawaz