2009-10-20 3 views
2

Мое заявление об отказе от ответственности заключается в том, что я начал преподавать C++ около недели назад, и мой прежний опыт программирования был с динамическими языками (Python, javascript).C++ Template + Iterator (noob question)

Я пытаюсь перебирать, хотя содержимое вектора с использованием обобщенной функции для вывода элементов:

#include <iostream> 
#include <algorithm> 
#include <vector> 

using std::vector; 
using std::cout; 

template <class T> 
void p(T x){ 
    cout << x; 
} 

int main() { 

    vector<int> myV; 

    for(int i = 0; i < 10; i++){ 
     myV.push_back(i); 
    } 

    vector<int>::const_iterator iter = myV.begin(); 

    for_each(iter, myV.end(), p); 

    return 0; 
} 

код не компилируется. Кто-нибудь объяснит, почему?

Edit: Ошибка компилятора:

error: no matching function for call to 'for_each(_gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<const int, _gnu_norm::vector<int, std::allocator<int> > >, __gnu_debug_def::vector<int, std::allocator<int> > >&, __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int, __gnu_norm::vector<int, std::allocator<int> > >, __gnu_debug_def::vector<int, std::allocator<int> > >, <unknown type>)'

Спасибо!

+5

Что компилятор вы получаете? –

ответ

13

Try:

for_each(myV.begin(), myV.end(), p<int>); 

Существовали две ошибки в коде:

  • Итераторы не были того же типа
  • Указатель функции не был на самом деле является указателем.
    • Обычно шаблонные функции могут быть выведены из этого параметра. Но в этом случае вы на самом деле не используете его, вы передаете его (или его адрес) функции (при этом нормальные правила вывода функции шаблона не работают). Поскольку компилятор не может определить, какую версию функции «p» вам нужно использовать, вы должны быть явным.

Существует также хороший выход итератор, который делает это:

std::copy(myV.begin(),myV.end(), std::ostream_iterator<int>(std::cout)); 

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

template<typename T> 
struct P 
{ 
    void operator()(T const& value) const 
    { 
     std::cout << value; 
    } 
}; 

.... 

for_each(myV.begin(), myV.end(), P<int>()); 

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

+4

Можете добавить, что p обозначает определенную функцию, для которой компилятор может создавать код. Просто писать p не говорит, какой тип T (int, float или rhubarb_pie?), А система типов C++ не делает так много (свободно сформулированная, она имеет тенденцию идти сверху вниз, а не снизу вверх). – Macke

+0

Прочитайте комментарий Маркуса выше для почему :-) –

+1

Я бы также мог иметь 'struct P {template void oeprator() (const T & value) const {std :: cout << значение; }}; 'в сочетании с' for_each (myV.begin(), myV.end(), P()); '. Будет ли это более или менее эффективным? –

0

Я не использовал for_each в C++, но я хотел бы написать тот же цикл так:

 
vector<int>::iterator iter; 

for(iter = myV.begin(); iter != myV.end(); iter++) { 
    p(iter); 
} 
+3

предпочитает приращение приставки: ++ iter –

+0

В чем разница, я думаю, что в этой ситуации это вопрос стиля кодирования программиста. – giolekva

+1

Postfix increment делает копию, которая может вызвать проблемы с производительностью. –

3

Причина, по которой работает решение Мартина и ваш не в том, что p шаблон функции, а не фактическая функция. Функция шаблона не имеет адреса, который вы можете взять, или перейти к функции. Вы должны создать экземпляр функции шаблона, чтобы создать фактическую функцию, которую вы можете использовать.

+0

Я думаю, что это больше связано с тем, что компилятор не является abel, чтобы вывести тип P (потому что это шаблон). Вы можете вручную инициировать несколько версий p. Но здесь недостаточно контекста для компилятора, чтобы определить, какую версию p использовать. Если вы вызовете компилятор «p», вывели бы тип и, следовательно, адрес, основанный на параметрах. т.е. p (5); является действительным вызовом. –

+0

Шаблоны функций не имеют типа. Они просто не существуют, пока не будут созданы экземпляры. Точно так же шаблоны классов не являются типами, но являются экземплярами. – cdiggins

+0

Исправить. Каждая версия функции уникальна. Но компилятор пытается вывести, какую функцию вызывать, даже если существует несколько версий, но в этом контексте недостаточно информации для определения того, какую инстанцию ​​использовать. –

1

Проблема заключается в том, что вы должны передать некоторую «вызываемую сущность» до std::for_each. Это может быть объект функции (это класс, перегружающий оператор вызова функции) или указатель на функцию. (Там, где необходим указатель функции, вы можете передать имя функции с соответствующим прототипом - имена функций неявно преобразуются в адреса функций.)

Однако, ваш p не является функцией, это функция template. И шаблон функции - это просто: шаблон для создания функций из. Вам нужно передать такую ​​созданную функцию вместо имени шаблона. Механизм, позволяющий компилятору создать функцию из шаблона функции, часто называется созданием шаблона . Поэтому вам нужен экземпляр шаблона. Это создается компилятором неявно всякий раз, когда вы используете такой экземпляр.

Так что, как другие уже говорили, что вам нужно явно передать p<int> в std::foreach:

ошибки
std::for_each(myV.begin(), myV.end(), p<int>);