Consider the following code sample:
void my_print(bool is_red, const char* format, ...){
va_list args;
va_start(args, format);
if(is_red)
print_red(format, args);
else
print_normal(format, args);
va_end(args);
}
void my_print(const char* format, ...){
va_list args;
va_start(args, format);
print_normal(format, args);
va_end(args);
}
int main() {
my_print((const char*)"Hello %s\n", "World");
return 42;
}
This code is ambiguous to the compiler and it yields the error:
more than one instance of overloaded function "my_print" matches the argument list: function "my_print(bool is_red, const char *format, ...)" (declared at line 12) function "my_print(const char *format, ...)" (declared at line 22) argument types are: (const char *, const char [6])
From what I understand the parameters I passed can interpreted as either (const char*, ...) where the '...' is just 'const char*' or (bool, const char*, ...) where the '...' is empty.
I would expect the compiler to assume I want to call the one which does not require an implicit cast from 'bool' to 'const char*' (which I do), and hence call the second instance.
How can I clarify this ambiguity to the compiler without changing the syntax of the function call?
CodePudding user response:
You could rename both overloads of my_print
to functions named differently and introduce a template names my_print
instead, which allows you to add a check, if the type of the first parameter is char const*
in an if constexpr
to resolve the ambiguity.
void print_red(char const* format, va_list lst)
{
printf("red :");
vprintf(format, lst);
}
void print_normal(char const* format, va_list lst)
{
printf("normal :");
vprintf(format, lst);
}
void my_print_with_color_impl(bool is_red, const char* format, ...) {
va_list args;
va_start(args, format);
if (is_red)
print_red(format, args);
else
print_normal(format, args);
va_end(args);
}
void my_print_impl(const char* format, ...) {
va_list args;
va_start(args, format);
print_normal(format, args);
va_end(args);
}
template<class T, class ... Args>
void my_print(T first, Args ... args)
{
if constexpr (std::is_same_v<std::decay_t<T>, char const*>)
{
my_print_impl(first, args...);
}
else
{
my_print_with_color_impl(first, args...);
}
}
int main() {
my_print("Hello %s\n", "World");
return 42;
}
Alternatively pre C 17 you could create a template class for printing and partially specialize it based on the first parameter:
template<class T, class...Args>
struct Printer
{
void operator()(bool is_red, char const* format, ...) const
{
va_list args;
va_start(args, format);
if (is_red)
print_red(format, args);
else
print_normal(format, args);
va_end(args);
}
};
template<class...Args>
struct Printer<char const*, Args...>
{
void operator()(char const* format, ...) const
{
va_list args;
va_start(args, format);
print_normal(format, args);
va_end(args);
}
};
template<class T, class ... Args>
void my_print(T first, Args ... args)
{
(Printer<typename std::decay<T>::type, Args...> {})(first, args...);
}
int main() {
my_print("Hello %s\n", "World");
my_print(1, "Hello colorful %s\n", "World");
return 42;
}