使用可变函数模板的奇怪重载决议
Weird overload resolution with variadic function templates
我有以下代码:
#include <iostream>
template <typename... Args>
void f(int a, int b, Args... args) {
std::cout << b << '\n';
f(a, args...);
}
void f(int, int b) {
std::cout << b << '\n';
}
int main() {
f(1, 2);
//f(1, 2, 3);
}
f(1, 2)
编译,f(1, 2, 3)
不编译。从编译器生成的错误消息中,我看到 f<>
正在以某种方式被实例化。在实例化中,调用 f(a)
并因此产生错误。是什么让编译器在解析调用f(1, 2, 3)
的过程中不使用f(int, int)
而是尝试实例化f<>(int, int)
?
在可变参数函数模板 f()
中,由于 [temp.dep],递归调用中的 f
是依赖名称,强调我的:
In an expression of the
form:
postfix-expression ( expression-listopt)
where the postfix-expression is an unqualified-id, the unqualified-id denotes a dependent name if
(1.1) — any of the expressions in the expression-list is a pack expansion (14.5.3),
并且,根据 [temp.dep.res],强调我的:
In resolving dependent names, names from the following sources are considered:
(1.1) — Declarations that are visible at the point of definition of the template.
(1.2) — Declarations from namespaces associated with the types of the function arguments both from the
instantiation context (14.6.4.1) and from the definition context.
只有一个 f
的声明在 template <typename... Args> void f(int, int, Args...)
的定义点可见,那就是它本身。第二点在这里不适用,因为您的所有参数都是 int
s 并且没有用于基本类型的关联名称空间。由于无法使用单个参数调用该函数模板,因此会出现编译错误。
解决方案是重构您的代码,以便您的基本情况在定义点可见,即:
// this can be just the declaration
void f(int, int ) { /* ... */ }
template <typename... Args>
void f(int a, int b, Args... args)
{
std::cout << b << '\n';
f(a, args...); // now this will call f(int, int)
// if sizeof...(Args) == 1
}
应用 (1.2) 的示例如下:
#include <iostream>
template <typename A, typename... Args>
void f(A a, int b, Args... args) {
std::cout << b << '\n';
f(a, args...);
}
template <typename A>
void f(A a, int b) {
std::cout << b << '\n';
}
struct bar {};
int main() {
//f(1,2,3); // still doesn't compile, same reasoning
f(bar{}, 2, 3); // OK. bar is in the global namespace, so declarations
// from the global namespace in both instantiation
// and definition context are considered, which includes
// the second `f`.
}
我有以下代码:
#include <iostream>
template <typename... Args>
void f(int a, int b, Args... args) {
std::cout << b << '\n';
f(a, args...);
}
void f(int, int b) {
std::cout << b << '\n';
}
int main() {
f(1, 2);
//f(1, 2, 3);
}
f(1, 2)
编译,f(1, 2, 3)
不编译。从编译器生成的错误消息中,我看到 f<>
正在以某种方式被实例化。在实例化中,调用 f(a)
并因此产生错误。是什么让编译器在解析调用f(1, 2, 3)
的过程中不使用f(int, int)
而是尝试实例化f<>(int, int)
?
在可变参数函数模板 f()
中,由于 [temp.dep],递归调用中的 f
是依赖名称,强调我的:
In an expression of the form:
postfix-expression ( expression-listopt)
where the postfix-expression is an unqualified-id, the unqualified-id denotes a dependent name if
(1.1) — any of the expressions in the expression-list is a pack expansion (14.5.3),
并且,根据 [temp.dep.res],强调我的:
In resolving dependent names, names from the following sources are considered:
(1.1) — Declarations that are visible at the point of definition of the template.
(1.2) — Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.
只有一个 f
的声明在 template <typename... Args> void f(int, int, Args...)
的定义点可见,那就是它本身。第二点在这里不适用,因为您的所有参数都是 int
s 并且没有用于基本类型的关联名称空间。由于无法使用单个参数调用该函数模板,因此会出现编译错误。
解决方案是重构您的代码,以便您的基本情况在定义点可见,即:
// this can be just the declaration
void f(int, int ) { /* ... */ }
template <typename... Args>
void f(int a, int b, Args... args)
{
std::cout << b << '\n';
f(a, args...); // now this will call f(int, int)
// if sizeof...(Args) == 1
}
应用 (1.2) 的示例如下:
#include <iostream>
template <typename A, typename... Args>
void f(A a, int b, Args... args) {
std::cout << b << '\n';
f(a, args...);
}
template <typename A>
void f(A a, int b) {
std::cout << b << '\n';
}
struct bar {};
int main() {
//f(1,2,3); // still doesn't compile, same reasoning
f(bar{}, 2, 3); // OK. bar is in the global namespace, so declarations
// from the global namespace in both instantiation
// and definition context are considered, which includes
// the second `f`.
}