模板和重载之间的交互

Interaction between templates and overloads

下面代码的输出是TA,但是我不明白为什么。

#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{});
}

我对模板的理解告诉我以下几点:

但是,以上是错误的,否则我会得到AA(其实,TT也比TA更让我吃惊)。

我也注意到把非模板重载移到caller的定义之前,输出变成了AA,所以我只能做出这样的猜想:

不过,我不知道以上是否有意义。

更可预测的是,如果我使用以下模板专业化

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

而不是非模板重载,那么输出总是AA,无论我是否放专业化在 caller 的定义之前或之后。

fun(A{}); 不涉及任何依赖于模板参数的表达式,因此查找和重载解析发生在定义点。那时,只有 fun(T) 可见; fun(A) 不参与重载决议。

fun(t) 调用依赖于 T,因此解析延迟到实例化点。那时,fun(A)fun(T) 都可见,fun(A) 获胜:非模板优先于模板,其他条件相同。

补充一下 Igor Tandetnik 的回答:如果您通过提供假参数延迟实例化,重载解析会延迟:

#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
}

这是延迟解析其他与模板参数无关的表达式的常用技巧,例如向模板添加假参数以防止特化被填满,例如:

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.

有时您需要使用 this-> 访问模板 class 成员以强制他们也依赖,等等。