tags:

views:

432

answers:

4

Hi,

I have some weird problem with templates. When trying to pass a parameterised iterator it complains that no function can be found. The code snippet is here, forget about the functionality, it's using the reference to the templatized iterator what interests me

#include <list>
#include <iostream>

template<typename T>
void print_list_element(typename std::list<T>::iterator& it){
    std::cout << *it << std::endl;
}

int main() {
    std::list<int> theList;
    theList.push_back(1);

    std::list<int>::iterator it = theList.begin();
    print_list_element(it);

    return 0;
}

If you try to compile this with g++ v4.3.2 it complains with a message saying that:

main.cpp:14: error: no matching function for call to 'print_list_element(std::_List_iterator<int>&)'

Is there something wrong with the code I wrote or is that g++ needs more information?

+9  A: 

g++ can't figure out which template overload of print_list_element it should use. If you explicitly specify the template parameter it works:

print_list_element<int>(it);
sth
+4  A: 

That is illegal because std::list< T >::iterator is not what the standard calls a proper deduced context

jab
I think the appropriate part of the '03 standard is: 14.8.2.4/4. Also the standard says that this is a "non-deduced context", rather than your wording above.
Richard Corden
+4  A: 

The other responses are correct, but for completeness I'll just add that, by design, C++ can only deduce template arguments automatically in certain cases, and this isn't one of them.

When you think about it, you'll realise that automatic deduction in this case would lead to an undesirable situation. std::list<T>::iterator is not a real type, it's just a typedef alias for a real type (e.g. it might be T*) to which it is immediately translated, so the compiler would have to build some sort of "reverse index" in order to map T* back to std::list<T>::iterator for automatic deduction of T to work here. But this mapping would break as soon as another class template was created that had a type member called iterator that was typedefed to T* -- then the compiler would have two choices of what to translate T* to, and no way to choose between them. Clearly, any automatic deduction policy that breaks when an unrelated class adds a particular typedef type member is much too fragile to work.

j_random_hacker
+5  A: 

A better way is to write the function like this:

template<typename Iter>
void print_element(Iter it){
    std::cout << *it << std::endl;
}

This will now work for any type of iterator, not just std::list<T>::iterator. Also, the template type will be deduced correctly from the argument.

I realize that it was a contrived example, but almost always you probably didnt want to pass in list<T>::iterator to a function anyways. At worst, at least template on ListType so that your code would work with lists with custom allocators.

Greg Rogers
+1. This will even work for plain arrays. If for some reason you really want it to work *only* for std::list<T>, look up Boost's enable_if<> template.
j_random_hacker