3

下面有一些非常基本的 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);
}

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

Godbolt 上的实时示例

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

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

4

1 回答 1

8

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

template <typename T=S> void foo(T);

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

  • foo({})无法推断T(因为{}没有类型),所以我们转而使用 的默认参数T,即S. 这给了我们一个可行的候选人来调用 - foo<S>

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

  • foo(0) 可以推断TTint。这给了我们一个可行的候选人来调用 - foo<int>。对此没有专门化,这 [误导性] 打印"S"(即使这里没有S涉及)。

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

如果你甚至想跟注foo(0)foo<S>你需要防止扣分成功int。但到那时,为什么还要有一个模板,只需要一个带有S.

于 2021-01-07T18:55:53.907 回答