I have two overloaded functions where the only difference is the last argument, in one case being a va_list
and in the other case being an ellipsis.
I noticed the selected overload is different depending if this last argument is an integer literal 0 or 1.
I made a simplified working example as follows, where I am experimenting with various inputs and found out some other weird scenarios:
#include <iostream>
#include <cstdarg>
void Func(va_list /*arg*/)
{
std::cout << "void Fun(va_list);" << std::endl;
}
void Func(...)
{
std::cout << "void Fun(...);" << std::endl;
}
int main()
{
std::cout << "Func(0);" << " >> ";
Func(0);
std::cout << "Func(1);" << " >> ";
Func(1);
std::cout << "Func(-1);" << " >> ";
Func(-1);
std::cout << "Func('0');" << " >> ";
Func('0');
std::cout << "Func('\\0');" << " >> ";
Func('\0');
std::cout << "Func(\"\");" << " >> ";
Func("");
std::cout << "Func(\"Dummy\");" << " >> ";
Func("Dummy");
std::cout << "Func(nullptr);" << " >> ";
Func(nullptr);
std::cout << "Func(0*1);" << " >> ";
Func(0*1);
std::cout << "Func(0.0);" << " >> ";
Func(0.0);
std::cout <<"int i = 0; Func(i);" << " >> ";
int i = 0;
Func(i);
std::cout <<"i = 1; Func(i);" << " >> ";
i = 1;
Func(i);
static const int j = 0, k = 1;
std::cout << "static const int j = 0; Func(j);" << " >> ";
Func(j);
std::cout << "static const int k = 1; Func(k);" << " >> ";
Func(k);
}
Compiling this code in cppshell with C++14 gives the following output:
Func(0); >> void Func(va_list);
Func(1); >> void Func(...);
Func(-1); >> void Func(...);
Func('0'); >> void Func(...);
Func('\0'); >> void Func(va_list);
Func(""); >> void Func(...);
Func("Dummy"); >> void Func(...);
Func(nullptr); >> void Func(va_list);
Func(0*1); >> void Func(va_list);
Func(0.0); >> void Func(...);
int i = 0; Func(i); >> void Func(...);
i = 1; Func(i); >> void Func(...);
static const int j = 0; Func(j); >> void Func(...);
static const int k = 1; Func(k); >> void Func(...);
As you can see, some calls resolve to the va_list
overload, while most resolve to the ellipsis overload.
I believe this weirdness may come from the fact that va_list
is often a char*
. In Visual C++, I find it defined in vadefs.h
as typedef char* va_list;
. However I still do not understand how the overload resolution is working on most cases here.
va_list
and ellipsis?Func(0)
resolve to the ellipsis overload?The ...
overload will only be selected if there is no implicit conversion sequence to va_list
. In other words, the ellipsis always has the lowest possible priority during overload resolution.
Since the definition of va_list
is unspecified, in general the results you observe will not be portable: Func(x)
could call either the va_list
overload or the ellipsis overload depending on the implementation. The one exception is that Func(x)
will always call the va_list
overload when x
itself has type va_list
(possibly const-qualified).
If, in fact, va_list
is char*
on your system, then a null pointer conversion can occur: in other words, 0 can be implicitly converted to the null pointer of type char*
. No other integer value can be implicitly converted to a pointer value.
As of C++14, even '\0'
should not be implicitly convertible to a pointer value. The null pointer conversion only applies to an integer literal with value zero. (Although '\0'
has integer type, it is not an integer literal.) This particular version of Visual Studio is out of date.
As for your question "How could i make a call to Func(0) resolve to the ellipsis overload?", well, you can't change the rules of overload resolution. What you can do is change the ellipsis overload to a template:
template <class T>
void Func(T x);
This guarantees that the va_list
overload will only be called when the argument is va_list
(possibly const-qualified) and in all other cases, the template will be called since it will give an exact match. Yet, a problem still remains: you may have some char*
variable, which you think of as not being a va_list
, and calling Func
with it as an argument could call the va_list
overload since va_list
is char*
(on your system). There's no way to prevent this. Maybe you shouldn't be overloading Func
at all. You can have two separate functions instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With