使用显式模板参数列表和 [temp.arg.explicit]/3 的函数调用的演绎失败

Deduction failure of function call with explicit template argument list and [temp.arg.explicit]/3

本文关键字:explicit arg 演绎 函数调用 temp 失败 列表 参数      更新时间:2023-10-16

[temp.arg.explicit]/3 的 C++17 标准(最终草案)说到了使用显式指定的模板参数列表推导函数模板参数

在推导完成但失败的上下文中,或者 [...],如果指定了模板参数列表,并且它与任何默认模板参数一起标识单个函数模板专用化,则 template-id 是函数模板专用化的左值。

这如何应用于参数包?

考虑

template<typename...>
struct S {
S(int) {}
};
template<typename... A>
void f(S<A...>) {}
int main() {
f<int>(0);
}

这在 MSVC 上编译,但不在 GCC 和 Clang 上编译,参见 godbolt。我的直觉也认为它应该失败,因为演绎会失败,但上面的引用似乎暗示即使演绎失败,因为f<int>(在我的理解中)唯一标识模板专用化,f<int>应该考虑引用该专用化,然后调用它,没有重载解决方案,这将起作用, 隐式将0转换为S<int>

我对报价的理解有什么问题,或者 MSVC 确实正确?


请注意,如果我们尝试调用f<>(0);(我想应该按照上面的考虑工作),所有三个编译器都拒绝编译。

与该问题相关的还有 [temp.arg.explicit]/6,它告诉我们函数参数的隐式转换(如上所示)是

如果参数类型不包含参与模板参数推导的模板参数。[ 注意:如果明确指定了模板参数,则模板参数不参与模板参数推导。 [...]]

所以,现在的问题是A...是否参与模板参数推导。(在这一点上,我想指出,如果我们用一个模板参数替换参数包,OPs 代码也会在 gcc/clang 下编译,因为它是显式指定的)。

有人可能会争辩说,A...是明确规定的,因此不参与演绎。但我认为一个是错误的。[temp.arg.explicit]/9 告诉我们,演绎可以扩展显式指定的模板参数列表。因此,f<int>(S<int, char>{0});是有效的,A...被推导出为int, char。所以在这种情况下,A...肯定会参与扣除。但是,由于此调用仅与参数上的调用不同,因此推导也必须在调用中进行。

换句话说,f<int>(0);也可能意味着调用f<int, char>,因此,它不指定单个函数模板规范。

这无关紧要。没有"没有重载解决"的函数调用。CWG2092清楚地表明了这一点。

[temp.over]/1 控件(为便于阅读而分开;强调我的):

写入对函数

或函数模板名称的调用时 (显式或隐式使用运算符表示法),模板 参数推导([temp.deduc])和检查任何明确的 为每个函数执行模板参数 ([temp.arg]) 模板以查找可以是 与该函数模板一起使用以实例化函数模板 可以使用调用参数调用的专用化。

对于每个函数模板,如果参数推导和检查成功,模板参数s(推导和/或显式)用于 合成单个函数模板的声明 添加到候选函数集的专业化 用于重载解析。

如果,对于给定的函数模板, 参数推导失败或合成函数模板失败 专业化格式不正确,没有这样的功能添加到 该模板的候选函数集。全套 候选函数包括所有合成声明和所有 同名的非模板重载函数。这 合成声明被视为与 过载解析的其余部分,除非在 [过度.匹配.最佳]。

有一个开放的核心语言问题(问题 2055:显式指定的非推导参数包)与此情况密切相关。

根据我对这个问题中隐含的内容的理解,目的是编译器应该表现为 MSVC,但据说标准不够清晰。