将可变参数模板与 C 样式可变参数函数混合时的模板参数推导
Template argument deduction when mixing variadic template with C-style variadic function
受this answer的启发,我生成了这段代码,其输出取决于编译器:
template <typename... Args>
constexpr auto foo(Args&& ...args, ...) noexcept {
return sizeof...(args);
}
constexpr auto bar() noexcept {
return (&foo<int>)(1, 2);
}
如果使用 GCC 11 编译,bar
调用 foo<int>
和 returns 1,而 clang 13 和 MSVC 2019 都推导出 foo<int, int>
和 bar
returns 2.
这是我在 Godbolt 上的沙箱:https://godbolt.org/z/MedvvbzqG。
哪个输出是正确的?
编辑:
如果我直接使用 return foo<int>(1, 2);
,即使用
,错误行为仍然存在
constexpr auto bar() noexcept {
return foo<int>(1, 2);
}
编辑:问题编辑后,现在包含两个正交的子问题,我分别处理了。
给定foo<int>(1, 2)
,是否应该推导参数包以覆盖所有参数?
是的。 parameter pack确实出现在parameter-declaration-list的末尾,这是判断是否it's non-deduced or not. This was actually clarified in CWG issue 1569的标准。我们可以通过观察所有编译器都认为这是好的来说服自己:
template <typename... Args>
constexpr auto foo(Args&& ...args, ...) noexcept {
return sizeof...(args);
}
static_assert(2 == foo(1, 2), "always true");
只有当我们将foo
更改为foo<int>
时,GCC突然停止推导包。它没有理由这样做,明确地向包提供模板参数不应该影响它是否符合扣除条件。
(&T<...>)(...)
形式的调用是否仍然调用模板参数推导?
答案是肯定的,正如开放 CWG 问题 1038 中所讨论的那样:
A related question concerns an example like
struct S {
static void g(int*) {}
static void g(long) {}
} s;
void foo() {
(&s.g)(0L);
}
Because the address occurs in a call context and not in one of the contexts mentioned in 12.3 [over.over] paragraph 1, the call expression in foo is presumably ill-formed. Contrast this with the similar example
void g1(int*) {}
void g1(long) {}
void foo1() {
(&g1)(0L);
}
This call presumably is well-formed because 12.2.2.2 [over.match.call] applies to “the address of a set of overloaded
functions.” (This was clearer in the wording prior to the resolution
of issue 704: “...in this context using &F behaves the same as using
the name F by itself.”) It's not clear that there's any reason to
treat these two cases differently.
正如注释所解释的,在 issue 704 之前我们有这个非常明确的部分:
The fourth case arises from a postfix-expression of the form &F
, where F
names a set of overloaded functions. In the
context of a function call, &F
is treated the same as the name F
by itself. Thus, (&F)(
expression-listopt )
is simply
F(
expression-listopt )
, which is discussed in 13.3.1.1.1.
这个措辞最终被删除的原因不是它有缺陷,而是整个部分措辞不当。新的措辞仍然明确指出重载决议应用于重载集的地址(这就是我们这里的内容):
If the postfix-expression denotes the address of a set of overloaded functions and/or function templates, overload resolution is applied using that set as described above.
受this answer的启发,我生成了这段代码,其输出取决于编译器:
template <typename... Args>
constexpr auto foo(Args&& ...args, ...) noexcept {
return sizeof...(args);
}
constexpr auto bar() noexcept {
return (&foo<int>)(1, 2);
}
如果使用 GCC 11 编译,bar
调用 foo<int>
和 returns 1,而 clang 13 和 MSVC 2019 都推导出 foo<int, int>
和 bar
returns 2.
这是我在 Godbolt 上的沙箱:https://godbolt.org/z/MedvvbzqG。
哪个输出是正确的?
编辑:
如果我直接使用 return foo<int>(1, 2);
,即使用
constexpr auto bar() noexcept {
return foo<int>(1, 2);
}
编辑:问题编辑后,现在包含两个正交的子问题,我分别处理了。
给定foo<int>(1, 2)
,是否应该推导参数包以覆盖所有参数?
是的。 parameter pack确实出现在parameter-declaration-list的末尾,这是判断是否it's non-deduced or not. This was actually clarified in CWG issue 1569的标准。我们可以通过观察所有编译器都认为这是好的来说服自己:
template <typename... Args>
constexpr auto foo(Args&& ...args, ...) noexcept {
return sizeof...(args);
}
static_assert(2 == foo(1, 2), "always true");
只有当我们将foo
更改为foo<int>
时,GCC突然停止推导包。它没有理由这样做,明确地向包提供模板参数不应该影响它是否符合扣除条件。
(&T<...>)(...)
形式的调用是否仍然调用模板参数推导?
答案是肯定的,正如开放 CWG 问题 1038 中所讨论的那样:
A related question concerns an example like
struct S { static void g(int*) {} static void g(long) {} } s; void foo() { (&s.g)(0L); }
Because the address occurs in a call context and not in one of the contexts mentioned in 12.3 [over.over] paragraph 1, the call expression in foo is presumably ill-formed. Contrast this with the similar example
void g1(int*) {} void g1(long) {} void foo1() { (&g1)(0L); }
This call presumably is well-formed because 12.2.2.2 [over.match.call] applies to “the address of a set of overloaded functions.” (This was clearer in the wording prior to the resolution of issue 704: “...in this context using &F behaves the same as using the name F by itself.”) It's not clear that there's any reason to treat these two cases differently.
正如注释所解释的,在 issue 704 之前我们有这个非常明确的部分:
The fourth case arises from a postfix-expression of the form
&F
, whereF
names a set of overloaded functions. In the context of a function call,&F
is treated the same as the nameF
by itself. Thus,(&F)(
expression-listopt)
is simplyF(
expression-listopt)
, which is discussed in 13.3.1.1.1.
这个措辞最终被删除的原因不是它有缺陷,而是整个部分措辞不当。新的措辞仍然明确指出重载决议应用于重载集的地址(这就是我们这里的内容):
If the postfix-expression denotes the address of a set of overloaded functions and/or function templates, overload resolution is applied using that set as described above.