是什么让一个重载函数比另一个更受欢迎?

What makes an overloaded function prefered to another?

我实现了一个简单的 BooleanVariable class,其中我从模板参数 V 获取布尔值,它是比较函数 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== 完美地工作,我想知道为什么这两个比较函数在 V1 == V2 的情况下没有歧义问题,因为它们都适合模板。为什么是

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

V1 == V2时没有触发?有人可以给我解释一下吗?

在运算符==的情况下,两个参数都是(模板)类型BooleanVariable,没有歧义。 operator && 的两个变体有一个模板类型的参数和一个 BooleanVariable 类型的参数。第一个变量的第一个参数是 BooleanVariable,第二个变量的第二个类型是 BooleanConstant。这使得这两个定义都非常适合被选择,这就是编译器在您编写 BooleanVariable() && BooleanVariable() 时警告您的歧义。如果你仔细观察这两个变体完全匹配表达式并且没有什么东西可以区分它们,这不完全是运算符 == 的情况,但是 - 在这里当 BooleanVariable 类型的两个参数都具有具有相同值的模板非类型参数时选择第一个变体,否则当它们不同时选择第二个(带有两个 bool 模板非类型参数)。

对于涉及模板的重载决议,一个好的经验法则是更专业的候选者比更通用的候选者更受欢迎。例如:

void foo(int);

template <class T>
void foo(T);

如果您像 foo(42) 一样调用 foo,将调用非模板版本,因为它更专业。如果您使用任何其他类型调用它,将调用模板版本。

另一个例子:

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

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

这里,第二个模板显然更通用。因此,对于两个参数类型相同的情况,第一个模板将是首选。对于其他情况,将首选第二个模板。

这同样适用于您的示例。第一个运算符只能处理相同类型的参数,而第二个运算符可以处理不同类型和相同类型的参数。因此,如果可能,将调用第一个。否则会调用第二个。