当其中一个函数未编译时,函数重载查找如何工作?

How function overload lookup works when one of the functions does not compile?

所以我很不明白编译器是如何选择函数重载的。我以为我明白了这段代码:

#include <iostream>

using namespace std;

template<class T>
struct base_type:
    public T
{
};

template<class T>
void check(T (&)[sizeof(base_type<T>)/sizeof(base_type<T>)]) {std::cout << "yeah";}

template<class T>
void check(T) {std::cout << "nah";}

union U{};
struct S{};

int main()
{
    U u[1];
    S s[1];

    check(u); // compile error
    check(s);

    return 0;
}

为什么编译器没有选择 check(T) 的 2d 重载,因为它无法检查拳头重载中的数组引用的大小?

SFINAE 出现在声明处,而不是定义处,base_type<U> 的声明很好,但会为其定义产生硬错误。

标准提供特征 std::is_unionstd::enable_if 来做 SFINAE:

template<class T, std::enable_if_t<!std::is_union<T>::value, int> = 0>
void check(T (&)[1]) {std::cout << "yeah";}

对于 C++17,您甚至可以直接在函数内部使用 if constexpr(即使简单的 if 在您的简化情况下也可以):

template<class T>
void check(T (&)[1])
{
    if constexpr(std::is_union<T>::value) {
        std::cout << "nah";
    } else {
        std::cout << "yeah";
    }
}

只有来自 immediate context 的错误会导致 "soft" 错误,从而将函数模板从重载集中删除。编译器必须先实例化 base_type<T>,然后才能计算 sizeof(base_type<T>),并且该实例化导致的任何错误在直接上下文中都是 而不是 ,并导致硬错误。

我不确定你到底想做什么,但你可以使用 std::enable_if_t<std::is_union_v<T>> 来禁用过载。这样做的原因是首先完成的 std::enable_if 的实例化不会导致错误;结果 class 可能根本不包含名为 type 的成员。对 type 的访问是 在直接上下文中。

我还找到了可能支持答案的标准引述。

标准引用,C++11 §14.8.2/8:

Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed. — end note ]