What makes an overloaded function prefered to another?

Viewed 94

I have implemented a simple BooleanVariable class, where I get the boolean value from template parameter V, and it's comparison function operator==:

#include <iostream>

template<bool V>
struct BooleanVariable {
    BooleanVariable() = default;

    constexpr bool operator()() const {
        return V;
    }
};

template<bool V>
bool operator==(const BooleanVariable<V> &, const BooleanVariable<V> &) {
    return true;
}

template<bool V1, bool V2>
bool operator==(const BooleanVariable<V1> &, const BooleanVariable<V2> &) {
    return false;
}

int main() {
    std::cout<<(BooleanVariable<true>() == BooleanVariable<false>())<<"\n";
    std::cout<<(BooleanVariable<false>() == BooleanVariable<true>())<<"\n";
    std::cout<<(BooleanVariable<true>() == BooleanVariable<true>())<<"\n";
    std::cout<<(BooleanVariable<false>() == BooleanVariable<false>())<<"\n";
    
    return 0;
}

operator== works perfectly, that I wonder why these 2 comparison functions don't have ambiguity problem for the case when V1 == V2, as both of fit to the template. Why is

template<bool V1, bool V2>
bool operator==(const BooleanVariable<V1> &, const BooleanVariable<V2> &) {
    return false;
}

not triggered when V1 == V2? Can someone explains to me?

2 Answers

A good rule of thumb for overload resolutions where templates are involved is that more specialized candidates are preferred over more generic candidates. For example:

void foo(int);

template <class T>
void foo(T);

If you call foo like foo(42), the non-templated version will be called since it is more specialized. If you call it with any other type, the templated version will be called.

Another example:

template <class T>
void foo(T, T);

template <class T, class Y>
void foo(T, Y);

Here, the second template is obviously more general. So, for cases where types of both arguments are the same, the first template will be preferred. For other cases, the second template will be preferred.

The same applies to your example. The first operator can only handle arguments of the same type while the second one can handle arguments of differing types as well as identical types. Thus, the first one will be called if possible. Otherwise the second one will be called.

In the case of the operator ==, both arguments are of (template) type BooleanVariable, no ambiguity. The two variants of operator && have one argument of a template type, and one of BooleanVariable. The first one has a BooleanVariable as the first arg, and the second variant has a BooleanConstant as the second type. That makes both definitions perfectly viable for being chosen and that's the ambiguity the compiler warns you about when you write BooleanVariable() && BooleanVariable(). If you look carefully both variants perfectly match the expression and does not something do differentiate them, This is not exactly the case of operator ==, though - here when both arguments of type BooleanVariable have their template non-type parameter with the same value the first variant is chosen, otherwise when they are different the second one (with two bool template non-type parameters) is chosen.

Related