2
votes

I have this simple function template that's supposed to take a container as its template argument and print the contents:

template <typename T>
void print(typename T::iterator &it1,typename T::iterator &it2)
{
  while (it1 != it2) {
    std::cout<<*it1<<" ";
    ++it1;
  }
}

I'm passing an iterator range (tipically the first and the off-the-end iterators) and I added the typename keyword to the function parameters because the compiler needs to know that I'm talking about a type and not a static member.

but when I pass a vector to the function the compiler says it can't find any match for the call (the closest match being the function itself) How can it be?

vector<double> dvec;
vector<double>::iterator it_b=dvec.begin();
vector<double>::iterator it_e=dvec.end();
print_it2(it_b,it_e);

the compiler says :

template argument deduction/substitution failed
                    could not deduce template parameter T
1
It would work with template <typename iterator> void print(iterator &it1, iterator &it2). The problem (for the compiler) is that you feed it an iterator and from that it cannot figure out that T must be vector<double>. - Bo Persson
@BoPersson yes, I tryed that and it works indeed, it just deduce the type to be vector<double>::iterator doesn't it? - Luca
Theoretically T could also be some type MySpecialContainer that uses the same iterator class that vector does. The compiler cannot know that without instantiating ALL templates and check if any of them has a matching iterator member. - Bo Persson

1 Answers

3
votes

typename T::iterator is a non-deduced context. The compiler has no way of knowing which T should be deduced because it doesn't understand the semantic connection between the type member iterator and a given T. It would need to search through all types to find a match and be able to somehow disambiguate collisions. This is unreasonable, so the standard doesn't allow it.

You could supply the type explicitly:

print<vector<double>>(it_b, it_e);
print<decltype(dvec)>(it_b, it_e); //c++11

But it would probably be easier to forget about the container from which the iterator came and let the compiler deduce the iterator type:

template <typename It>
void print (It& it1, It& it2);

Maybe you have a reason to pass the iterators by reference, but it seems like you should pass by value instead:

template <typename It>
void print (It it1, It it2);