The following program compiles successfully.
template<typename T>
T sum(T x) {
return x;
}
template<typename T, typename... Args>
T sum(T x, Args... args) {
return x sum(args...);
}
int main() {
sum(1, 2, 3, 4, 5);
}
However, when I switch the order in which the template functions are written, it no longer compiles:
template<typename T, typename... Args>
T sum(T x, Args... args) {
return x sum(args...);
}
template<typename T>
T sum(T x) {
return x;
}
int main() {
sum(1, 2, 3, 4, 5);
}
Why does this happen? Aren't both functions already defined prior to being called in main()? Why does the order in which they're written in matter?
CodePudding user response:
At the heart of your question is the following issue: when a function name, such as sum
, appears inside a function template, what function does it refer to?
You wrote:
Aren't both functions already defined prior to being called in main()? Why does the order in which they're written in matter?
You seem to have the following expectation: that when a function template is instantiated, and a function name such as sum
appears inside it, the compiler finds all overloads of sum
that have been declared so far. Thus, according to your theory, since the sum
function template is not instantiated until it is called in main
, at that point, both overloads of sum
are visible, so the recursive calls to sum
should consider both overloads and eventually select the unary one to terminate the recursion.
However, as you've seen, that's not the case. If the variadic sum
is declared first, it eventually recursively calls itself with a single argument (ignoring the unary sum
overload declared later), which then attempts to call sum()
(no arguments), leading to a compilation error.
In this example, it thus appears that when the compiler sees a call to sum
inside a function template, it only finds functions named sum
that have been declared at the point of the call. When you switched the order around, you made it so that the variadic sum
could never call the unary sum
.
Actually though, the truth is more complicated. The C standard requires a behaviour that is colloquially referred to as "two-phase lookup". What this means is:
- when the compiler sees the definition of the function template, it finds all functions named
sum
that have been declared so far, and - when the compiler later instantiates the function template, it uses argument-dependent lookup to find additional overloads of
sum
regardless of whether they were declared before or after the enclosing template.
In your code, sum
is only ever called with arguments of type int
, which does not belong to any namespace. Thus, the variadic sum
only finds itself in phase 1, and nothing in phase 2.