函数模板参数包不在参数列表的末尾
Function template parameter pack not at the end of the parameter list
以下代码编译运行正常
void foo() {
}
template <typename T, typename... Args>
void foo(T x, Args... args) {
cout << x << endl;
foo(args...);
}
// inside main()
foo(1,1,1);
其他代码无法编译:
void foo() {
}
template <typename... Args, typename T>
void foo(Args... args, T x) {
foo(args...);
cout << x << endl;
}
// inside main()
foo(1,1,1);
编译器说没有匹配函数来调用 foo(1,1,1)
并说 foo(Args... args, T x)
是一个候选者,但是模板参数 deduction/substitution 失败了,因为候选者需要 1 个参数,但提供了 3 个。
这种情况有没有任何编译器无法处理的歧义?这个编译错误对我来说似乎不合逻辑。也许这不是故意的,符合 C++ 标准?
Clang's error message 中有趣的部分是:
main.cpp:11:6: note: candidate template ignored: couldn't infer template argument 'T'
void foo(Args... args, T x) {
^
问题是参数包 Args...
在 到 T
之前出现。
Args...
是 "greedy",因此没有参数留给编译器推导 T
,因此失败。
引用标准(强调我的):
[temp.param]/11
A template parameter pack of a function template shall not be followed
by another template parameter unless that template parameter can be
deduced from the parameter-type-list of the function template or has a
default argument. [Example:
...
// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { } // error
template<class... T, class U> void g() { } // error
— end example]
(此答案基于)
按照标准,模板参数包如果用在不在参数列表末尾的函数参数包中,是不可推导的。
§14.8.2.1/1 从函数调用推导模板参数
[temp.deduct.call]:
When a function parameter pack appears in a non-deduced context
([temp.deduct.type]), the type of that parameter pack is never
deduced. [ Example:
template<class T1, class ... Types> void g1(Types ..., T1);
void h(int x, float& y) {
const int z = x;
g1(x, y, z); // error: Types is not deduced
g1<int, int, int>(x, y, z); // OK, no deduction occurs
}
— end example ]
关于非推导上下文,§14.8.2.5/5 从类型推导模板参数
[temp.deduct.type]:
A function parameter pack that does not occur at the end of the parameter-declaration-list.
所以foo(1,1,1);
失败的直接原因是没有推导模板参数Args
,这是使函数调用有效所必需的。
为了解释错误消息,未推导的模板参数包将被推导为空的模板参数序列[1],这意味着它将被省略。然后 foo(1,1,1);
失败,因为参数的数量不匹配,这就是编译器所抱怨的。
正如标准示例所示,您可以明确指定模板参数以避免类型推导,即使它不符合代码的初衷。如:
template <typename T, typename... Args>
void foo(Args... args, T x) {
}
int main() {
// inside main()
foo<int, int, int>(1, 1, 1);
}
Here一些附加信息。
[1] 我在标准中找不到关于此的直接表达。最接近的是this、"A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced to an empty sequence of template arguments."
以下代码编译运行正常
void foo() {
}
template <typename T, typename... Args>
void foo(T x, Args... args) {
cout << x << endl;
foo(args...);
}
// inside main()
foo(1,1,1);
其他代码无法编译:
void foo() {
}
template <typename... Args, typename T>
void foo(Args... args, T x) {
foo(args...);
cout << x << endl;
}
// inside main()
foo(1,1,1);
编译器说没有匹配函数来调用 foo(1,1,1)
并说 foo(Args... args, T x)
是一个候选者,但是模板参数 deduction/substitution 失败了,因为候选者需要 1 个参数,但提供了 3 个。
这种情况有没有任何编译器无法处理的歧义?这个编译错误对我来说似乎不合逻辑。也许这不是故意的,符合 C++ 标准?
Clang's error message 中有趣的部分是:
main.cpp:11:6: note: candidate template ignored: couldn't infer template argument 'T'
void foo(Args... args, T x) {
^
问题是参数包 Args...
在 到 T
之前出现。
Args...
是 "greedy",因此没有参数留给编译器推导 T
,因此失败。
引用标准(强调我的):
[temp.param]/11
A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument. [Example:
... // U can be neither deduced from the parameter-type-list nor specified template<class... T, class... U> void f() { } // error template<class... T, class U> void g() { } // error
— end example]
(此答案基于
按照标准,模板参数包如果用在不在参数列表末尾的函数参数包中,是不可推导的。
§14.8.2.1/1 从函数调用推导模板参数 [temp.deduct.call]:
When a function parameter pack appears in a non-deduced context ([temp.deduct.type]), the type of that parameter pack is never deduced. [ Example:
template<class T1, class ... Types> void g1(Types ..., T1); void h(int x, float& y) { const int z = x; g1(x, y, z); // error: Types is not deduced g1<int, int, int>(x, y, z); // OK, no deduction occurs }
— end example ]
关于非推导上下文,§14.8.2.5/5 从类型推导模板参数 [temp.deduct.type]:
A function parameter pack that does not occur at the end of the parameter-declaration-list.
所以foo(1,1,1);
失败的直接原因是没有推导模板参数Args
,这是使函数调用有效所必需的。
为了解释错误消息,未推导的模板参数包将被推导为空的模板参数序列[1],这意味着它将被省略。然后 foo(1,1,1);
失败,因为参数的数量不匹配,这就是编译器所抱怨的。
正如标准示例所示,您可以明确指定模板参数以避免类型推导,即使它不符合代码的初衷。如:
template <typename T, typename... Args>
void foo(Args... args, T x) {
}
int main() {
// inside main()
foo<int, int, int>(1, 1, 1);
}
Here一些附加信息。
[1] 我在标准中找不到关于此的直接表达。最接近的是this、"A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced to an empty sequence of template arguments."