将可变参数模板与 C 风格可变参数函数混合时的模板参数推导

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)

沙箱更新:https://godbolt.org/z/Wj757sc7b

Col*_*mbo 2

编辑:编辑问题后,它现在包含两个正交的子问题,我已经分别处理了。

\n

给定foo<int>(1, 2),是否应该推导出参数包以涵盖所有参数?

\n

是的。参数包确实出现在参数声明列表的末尾,这是判断它是否是非推导的标准。这实际上已在CWG 第 1569 期中得到澄清得到澄清。我们可以通过观察所有编译器都同意这很好来说服自己:

\n
template <typename... Args>\nconstexpr auto foo(Args&& ...args, ...) noexcept {\n    return sizeof...(args);\n}\n\nstatic_assert(2 == foo(1, 2), "always true");\n
Run Code Online (Sandbox Code Playgroud)\n

仅当我们更改foofoo<int>时,GCC 才会突然停止推算包。它没有理由这样做,向包显式提供模板参数不应影响它是否有资格进行推导。

\n

进行以下形式的调用(&T<...>)(...)仍然会调用模板参数推导吗?

\n

答案是肯定的,正如公开的 CWG 问题 1038 中所讨论的:

\n
\n

一个相关的问题涉及一个例子,比如

\n
struct S {\n    static void g(int*) {}\n    static void g(long) {}\n} s;\n\nvoid foo() {\n    (&s.g)(0L);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

因为该地址出现在调用上下文中,而不是在 12.3 [over.over] 第 1 段中提到的上下文之一中,所以 foo 中的调用表达式可能是格式错误的。将此与类似示例进行对比

\n
void g1(int*) {}\nvoid g1(long) {}\n\nvoid foo1() {\n    (&g1)(0L);\n} \n
Run Code Online (Sandbox Code Playgroud)\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
\n

正如注释所解释的,在问题 704之前之前,我们有一个非常明确的部分:

\n
\n

第四种情况源自形式的后缀表达式&F,其中F命名了一组重载函数。在函数调用的\n上下文中,与名称本身&F相同。F因此,(&F)( 表达式列表opt )就是\nF( 表达式列表opt ),这将在 13.3.1.1.1 中讨论。

\n
\n

该措辞最终被删除的原因并不是它有缺陷,而是整个部分的措辞很糟糕。新的措辞仍然明确指出重载解析应用于重载集的地址(这就是我们这里的内容):

\n
\n

如果后缀表达式表示一组重载函数和/或函数模板的地址,则如上所述使用该组应用重载解析。

\n
\n