Search code examples
c++overload-resolution

Can you (and should you) disambiguate a function call taking T and const reference to T?


If we have:

void foo(int) {}
void foo(const int&) {}

we cannot call foo like this:

foo(3);

because the call is ambiguous:

error: call of overloaded 'foo(int)' is ambiguous
40 |     foo(3);
   |          ^
note: candidate: 'void foo(int)'
36 | void foo(int) {}
   |      ^~~
note: candidate: 'void foo(const int&)'
37 | void foo(const int&) {}
   |      ^~~

What we can do is explicitly provide the correct overload, for example via a function pointer:

auto (*ptr)(const int&) -> void = foo;
ptr(3); // calls the "const int&" overload, obviously

However, that kind of defeats the purpose of convenient overloads. The question is - can I somehow disambiguate the call in a more... elegant? way? Are there ever cases where it would be desired to provide both overloads, for T and for const T&?


Solution

  • we cannot call foo like this: foo(3); because the call is ambiguous

    In general, that's exactly why developers avoid providing overloads that similar. That makes it hard to use the overloads. Generally developers will have a const T& and a T&& overload, since those are never ambiguous.

    However, that kind of defeats the purpose of convenient overloads.

    The purpose of convenient overloads is to allow the calling code to easily call the right function. Calling exactly one is fulfilling the purpose of convenient overloads. The annoyance of casting to a function pointer is a side effect of having overloads too close togeather and is not normal.

    The only exception I can think of is when you want to pass an overloaded set of functions to something else as a functoid, and let that caller pick which overload it wants.

    struct foo_functoid {
        void operator()(int v) {foo(v);}
        void operator()(const int& v) {foo(v);}
    };
    //or
    struct foo_functoid {
        template<class T>
        void operator()(T&& v) {foo(std::forward<T>(v));}
    };
    

    But unfortunately, for these functoids, there is no shortcut, they must be written explicitly by hand (with possibly minor assistance from macros)