J. M. J. M. - 29 days ago 14
C++ Question

What is c++ default behavior to resolve ambiguous calls with template of template parameter?

Consider the following piece of code:

#include <iostream>
#include <vector>

using namespace std;

template <typename T>
void f(T& a) {
cout << "a" << endl;
}
template <typename T>
void f(vector<T>& a) {
cout << "b" << endl;
}

int main(int argc, char** argv) {
vector<vector<int>> v;
f<vector<vector<int>>>(v);
f<vector<int>>(v);
f(v);
}


In the two first calls to f, I explicitly specify the template version I want to use, so I have control over which version of the function I want to call.

The third implicit call should be ambiguous because we have the choice as the two first calls showed. Surprisingly, g++ compiler did not insult me and did its job without complaining.

The output is

a
b
b


which means
f<vector<int>>
has been used for the third call.

So what is the default behavior for such cases? My theory is that it takes the most restrictive instance which would make sense in this case.
Is this true? Is this specified somewhere because I cannot trust randomness.

There is nothing special about the vector class here. I was actually implementing some graph algorithms, but since I may have different representations for the graph, for example as an adjacency list vector < vector < outgoingarc < weighttype >>> or as an arc list vector < arc < weighttype >> , and also different representations of an arc depending on whether it is a unweighted/weighted graph/flow network it may carry different type of information. I am trying to write generic code using templates but I am facing this kind of issue at the moment.

Thank you

Answer

Your second f is said to be more specialized than your first f, so the second f wins overload resolution when both are matches.

The exact definition of more specialized is given in gory detail in the Standard, but the basic idea is to figure out whether any call to one can always be matched by the other (but not vice-versa). That is, if you have a std::vector<X>& for the second f, you know it can also match T& for the first f. But if you have a general X& for the first f, there's no guarantee it will match std::vector<T>& for the second f.