Gio*_*ani 7 c++ variadic-functions language-lawyer variadic-templates template-argument-deduction
受这个答案的启发,我生成了这段代码,其输出取决于编译器:
template <typename... Args>
constexpr auto foo(Args&& ...args, ...) noexcept {
return sizeof...(args);
}
constexpr auto bar() noexcept {
return (&foo<int>)(1, 2);
}
Run Code Online (Sandbox Code Playgroud)
如果使用 GCC 11 编译,bar则调用foo<int>并返回 1,而 clang 13 和 MSVC 2019 都会推导foo<int, int>并bar返回 2。
这是我在 godbolt 上的沙箱:https ://godbolt.org/z/MedvvbzqG 。
哪个输出是正确的?
编辑:
如果我return foo<int>(1, 2);直接使用,即与
constexpr auto bar() noexcept {
return foo<int>(1, 2);
}
Run Code Online (Sandbox Code Playgroud)
编辑:编辑问题后,它现在包含两个正交的子问题,我已经分别处理了。
\nfoo<int>(1, 2),是否应该推导出参数包以涵盖所有参数?是的。参数包确实出现在参数声明列表的末尾,这是判断它是否是非推导的标准。这实际上已在CWG 第 1569 期中得到澄清得到澄清。我们可以通过观察所有编译器都同意这很好来说服自己:
\ntemplate <typename... Args>\nconstexpr auto foo(Args&& ...args, ...) noexcept {\n return sizeof...(args);\n}\n\nstatic_assert(2 == foo(1, 2), "always true");\nRun Code Online (Sandbox Code Playgroud)\n仅当我们更改foo为foo<int>时,GCC 才会突然停止推算包。它没有理由这样做,向包显式提供模板参数不应影响它是否有资格进行推导。
(&T<...>)(...)仍然会调用模板参数推导吗?答案是肯定的,正如公开的 CWG 问题 1038 中所讨论的:
\n\n\n一个相关的问题涉及一个例子,比如
\nRun Code Online (Sandbox Code Playgroud)\nstruct S {\n static void g(int*) {}\n static void g(long) {}\n} s;\n\nvoid foo() {\n (&s.g)(0L);\n}\n因为该地址出现在调用上下文中,而不是在 12.3 [over.over] 第 1 段中提到的上下文之一中,所以 foo 中的调用表达式可能是格式错误的。将此与类似示例进行对比
\nRun Code Online (Sandbox Code Playgroud)\nvoid g1(int*) {}\nvoid g1(long) {}\n\nvoid foo1() {\n (&g1)(0L);\n} \n这个调用大概是格式良好的,因为 12.2.2.2 [over.match.call] 适用于 \xe2\x80\x9c 一组重载\n函数的地址。\xe2\x80\x9d (这在之前的措辞中更清楚)问题 704 的解决方案:\xe2\x80\x9c...在此上下文中使用 &F 的行为与\n单独使用名称 F 的行为相同。\xe2\x80\x9d)清楚是否存在\'有什么理由以不同的方式对待这两种情况。
\n
正如注释所解释的,在问题 704之前之前,我们有一个非常明确的部分:
\n\n\n第四种情况源自形式的后缀表达式
\n&F,其中F命名了一组重载函数。在函数调用的\n上下文中,与名称本身&F相同。F因此,(&F)(表达式列表opt)就是\nF(表达式列表opt),这将在 13.3.1.1.1 中讨论。
该措辞最终被删除的原因并不是它有缺陷,而是整个部分的措辞很糟糕。新的措辞仍然明确指出重载解析应用于重载集的地址(这就是我们这里的内容):
\n\n\n如果后缀表达式表示一组重载函数和/或函数模板的地址,则如上所述使用该组应用重载解析。
\n