Enlico
Enlico

Reputation: 28510

Interaction between templates and overloads

The output of the following code is TA, but I don't understand why.

#include <iostream>
#include <type_traits>

struct A {};

template<typename T>
void fun(T) {
    std::cout << "T";
}

template<typename T>
void caller(T t) {
    fun(A{});
    fun(t);
}

void fun(A) {
    std::cout << "A";
}

int main() {
    caller(A{});
}

My understanding of templates tells me the following:

However, the above is wrong, otherwise I would get AA (actually, also TT would surprise me less than TA).

I've also noted that moving the non-template overload before the definition of caller, the output becomes AA, so I can only make this conjecture:

I don't know if the above makes any sense, though.

More predictably, if if I use the following template specialization

template<>
void fun<A>(A) {
    std::cout << "A";
}

instead of the non-template overload, then output is always AA, regardless of whether I put the specialization before or after caller's definition.

Upvotes: 4

Views: 61

Answers (2)

Lapshin Dmitry
Lapshin Dmitry

Reputation: 1124

Just to add to Igor Tandetnik answer: if you delay instantiation by providing fake parameters, the overload resolution is delayed:

#include <iostream>
#include <type_traits>

struct A {};

template<typename T>
void fun(T) {
    std::cout << "T";
}

template<typename T, typename AA = A> // <<==== HERE
void caller(T t) {
    fun(AA{});
    fun(t);
}

void fun(A) {
    std::cout << "A";
}

int main() {
    caller(A{}); // Does AA
}

This is a common trick to delay resolution of otherwise template parameter-independent expressions, for example adding fake parameters to templates to prevent specializations from being full, for example:

template<typename T>
struct foo {};

template<>
struct foo<void> {}; // Instantiated immediately

template<typename T, int fake = 0>
struct bar {};

template<int fake>
struct bar<void, fake> {}; // Not instantiated until used

// Users of bar<T> almost never realize they use bar<T, 0>, you can even provide a wrapper without `int` parameter.

Sometimes you need to access template class member with this-> to force them being dependant too, etc.

Upvotes: 2

Igor Tandetnik
Igor Tandetnik

Reputation: 52621

fun(A{}); doesn't involve any template parameter-dependent expressions, and so the lookup and overload resolution occurs at the point of definition. At that point, only fun(T) is visible; fun(A) does not participate in overload resolution.

fun(t) call depends on T, and so the resolution is delayed to the point of instantiation. At that point, both fun(A) and fun(T) are visible, and fun(A) wins: non-templates are preferred over templates, other things equal.

Upvotes: 2

Related Questions