C++ 函数模板重载:空大括号 vs 显式 int

Kon*_*rov 3 c++ c++20 function-templates-overloading

下面有一些非常基本的 C++ 函数模板重载解析示例(从实际代码中最小化)

struct S {
    S() {}
    S(int) {}
};

template <typename T = S>
void foo(T x) { std::cout << "S" << std::endl; }

template <>
void foo<S>(S x) { std::cout << "S spc" << std::endl; }

int main() {
    foo({});
    foo(0);
}
Run Code Online (Sandbox Code Playgroud)

这里我们有两种情况。在第一种情况下编译器应默认初始化某些东西(如 S)在第二种情况下将 int 转换为某些东西(如 S)

Godbolt 上的活生生的例子

我相信在这两种情况下,专业化都会赢得重载,因为专业化完全匹配,并且通过偏序 [temp.deduct.partial] 比主模板更专业

但是这个例子中的 clang 11 和 gcc 10.2 都同意在第二种情况下主模板获胜。这是两个编译器中的错误还是(可能)我对 C++ 标准不了解?

Bar*_*rry 8

只有一个候选人参与重载决议:

template <typename T=S> void foo(T);
Run Code Online (Sandbox Code Playgroud)

函数模板特化的选择发生在之后。在这种情况下,我们有:

  • foo({})无法推断T(因为{}没有类型),所以我们回退到使用 的默认参数T,即S。这为我们提供了一个可行的候选者 - foo<S>

    一旦我们决定调用foo<S>,我们就会考虑专业化。有一个,所以它被选中。这打印"S spc"

  • foo(0) 可以推断TTint。这为我们提供了一个可行的候选者 - foo<int>。对此没有专业化,这 [误导] 打印"S"(即使这里没有S涉及)。

默认模板参数仅在无法推导出实际模板参数或未明确提供时才起作用。

如果你甚至foo(0)想打电话foo<S>,你需要防止扣款成功int。但在这一点上,为什么甚至有一个模板,只是有一个带有S.