I was trying to understand difference between a raw pointer and an vector iterator. However, the following program trips me out. Does template function have priority over non-template function?
Expected: hello! world!
Actual: hello! hello!
#include <bits/stdc .h>
using namespace std;
template<typename It>
void foo(It begin, It end) {
cout << "hello! ";
}
void foo(const int* a, const int* b, size_t n=0) {
cout << "world! ";
}
int main() {
vector<int> A = {5,6,7,8,9};
int B[] = {1,2,3,4,5};
foo(A.begin(), A.end());
foo(B, B 5);
cout << endl;
}
CodePudding user response:
In general iterators of the class template std::vector
are not pointers (though in some early versions of compilers they were implemented as pointers).
For the both calls there will be called the template function. The call of the non-template function requires conversion to const (qualification conversion).
The non-template function would be called if the array was declared with the qualifier const like
const int B[] = {1,2,3,4,5};
On the other hand, if you will declare the vector with the qualifier const like
const vector<int> A = {5,6,7,8,9};
nevertheless the template function will be called because there will be used objects of the type std::vector<int>::const_iterator
that are not pointers to constant objects.
CodePudding user response:
iterator
is a set of classes that are used in various containers of the STL. Having this paradigm guarantees that you can use an iterator in template where the container is a template
template<typename Container> is_alone(const Container& c) {
Container::iterator i = c.begin();
if(i == c.end()) {
return false;
}
i;
return i == c.end();
}
This snippet above will work whether Container is a std::vector
, a std::unordered_map
, a std::set
, ...
How the iterators access the members of a container is left implantation-defined.
In concrete terms, a std::vector<T>::iterator
will almost always be implemented using a pointer, however this is hidden from the user of a std::vector<T>::iterator
, which only needs to know that iterator can be advanced, and can be accessed with operator*
.
For you snippet code, the C compiler will always target the function with the most specialized argument types that match the concrete argument type you're calling with.
Here it is int*
, so calling the template by setting It = int*
, is more specialized than calling the function with const int*
, so the template is preferred.
CodePudding user response:
Let's consider what is happening on case by case basis.
Case 1
Here we consider the call:
foo(A.begin(), A.end());
Here the arguments A.begin()
and A.end()
of the call expression are of type std::vector::iterator
but the parameter a
and b
of the nontemplate function are of type const int*
. Now, since there is no implicit conversion between the argument type std::vector::iterator
to the parameter type const int*
, the nontemplate function is not even viable for this call.
Thus, the template function can be used with its parameter deduced to std::vector::iterator
. And hence we get the first hello!
in the output.
Case 2
Here we consider the call:
foo(B, B 5);
In this case both the arguments are of type int*
(since array decay to pointer to their first element here).
Now, here there are two viable candidates.
First candidate is the nontemplate function with parameters a
and b
of type const int*
. But note that for this nontemplate function to be used a qualification conversion(from int*
to const int*
) is required.
Second candidate is the function foo<int *>(int *, int *)
generated from
the function template where the parameter is deduced to be int*
.
So we have two candidates. But as discussed above, the first candidate requires a conversion while the second candidate(which is generated from function template) does not require any conversion from argument type to parameter type. Thus, the second candidate is preferred over the first candidate and we get the second hello!
in the output.
Sidenote
If you were to change the type of the parameter of the nontemplate function to be int*
, then the nontemplate function would be preferred over the one generated from the function template and in this case you'll get the world!
as the output for the call foo(B, B 4)
.
template<typename It> //#1
void foo(It begin, It end) {
cout << "hello! ";
}
//-------vvv-----vvv----------------------->changed to int*
void foo(int* a, int* b, size_t n=0) {//#2
cout << "world! ";
}
int main() {
vector<int> A = {5,6,7,8,9};
int B[] = {1,2,3,4,5};
foo(A.begin(), A.end()); //calls #1
foo(B, B 4); //calls #2
cout << endl;
}