Reputation: 1216
Next code will compile with error: call of overloaded 'f(int)' is ambiguous
Can I resolve this without using va_list
when I call f
with one parameter?
It is for a printf kind of function. I just wondering if I can make more efficient with handling the simple case separately without introducing a new name.
Is there any workaround?
#include <cstdarg>
void f(int n, ...) {
va_list args;
va_start(args, n);
//Do something
va_end(args);
}
void f(int n) {
//Do something without va_list
}
int main() {
f(42);
return 0;
}
Upvotes: 2
Views: 466
Reputation: 170203
You can't have that overload (plainly), since it will always be ambiguous. The following paragraphs in the standard are pertinent, I think:
1 From the set of candidate functions constructed for a given context ([over.match.funcs]), a set of viable functions is chosen, from which the best function will be selected by comparing argument conversion sequences for the best fit ([over.match.best]). The selection of viable functions considers relationships between arguments and function parameters other than the ranking of conversion sequences.
2 First, to be a viable function, a candidate function shall have enough parameters to agree in number with the arguments in the list.
If there are m arguments in the list, all candidate functions having exactly m parameters are viable.
A candidate function having fewer than m parameters is viable only if it has an ellipsis in its parameter list ([dcl.fct]). For the purposes of overload resolution, any argument for which there is no corresponding parameter is considered to “match the ellipsis” ([over.ics.ellipsis]) .
A candidate function having more than m parameters is viable only if the (m+1)-st parameter has a default argument.130 For the purposes of overload resolution, the parameter list is truncated on the right, so that there are exactly m parameters.
So both overloads are viable functions to call. Now the compiler has to determine which is a better candidate based on the conversion sequences for the argument you provided. And they are both equally good in that regard, no conversion is required for 42 in either case.
Let's take advantage of the other overloading rules. A template function that is generated and found in overload resolution is considered a lesser match than a non-template function. So let's make the ellipsis version a template:
#include <iostream>
#include <type_traits>
#include <cstdarg>
void vf(int n, std::va_arg args) {
//Do something with args
}
template<typename T>
auto f(T n, ...)
-> std::enable_if_t<std::is_same<T, int>::value> {
std::va_list args;
va_start(args, n);
vf(n, args);
va_end(args);
}
void f(int n) {
//Do something without va_list
}
int main() {
f(42);
f(42, 53);
return 0;
}
But we'll apply SFINAE to the return type, so it could only be instantiated when T is an int (thus giving you your original function). Now the ellipsis version is chosen only when the single parameter version is not viable anymore, i.e. when there is more than one parameter.
Upvotes: 4