Home > Enterprise >  unwanted implicit cast makes overload ambiguous
unwanted implicit cast makes overload ambiguous

Time:10-23

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;
}
  • Related