Tomilov Anatoliy
Tomilov Anatoliy

Reputation: 16711

Ambiguity between const best matching function and other function

Let us consider following example:

#include <type_traits>

#if 1
struct X {};

struct O
{
    O(X) { ; }
};
#else
struct O {};

struct X
{
    operator O () { return {}; }
};
#endif

static_assert(std::is_convertible< X, O >::value);

struct S
{
    void f(X) const { ; }
    void f(O) { ; }
};

#include <cstdlib>

int
main()
{
    S s;
    s.f(X{});
    return EXIT_SUCCESS;
}

Live example

It gives an error:

 error: call to member function 'f' is ambiguous

When I removing const-qualifier, then the error cease to exist. Equally the same happens, if I add const-qualifier to the second overloading of f. I.e. if both overloadings are equally const-qualified, then all is OK.

Why is it so?

My compiler is clang 3.8.

Upvotes: 5

Views: 187

Answers (3)

Vlad from Moscow
Vlad from Moscow

Reputation: 310980

Member functions have implicit parameter this.

So to call one of the functions f the compiler needs either to convert this to type const S * or to convert X to O.

Neither conversion regarding all parameters is better. So the compiler issues an error.

Upvotes: 2

0xFFFFFFFF
0xFFFFFFFF

Reputation: 852

Mark B & Vlad from Moscow answer why, I just reply how can you call them.

you should add explicit to avoid compiler implicit conversion

#include <iostream>

struct X {};

struct O
{
    explicit O(X) { ; }

    O() = default;
    O(O const &) = default;
    O(O &&) = default;
};

struct S
{
    void f(X) const { ; }
    void f(O) { ; }
};

#include <cstdlib>

int main()
{
    S s;
    s.f(X{});
    return EXIT_SUCCESS;
}

EDIT

if your type O is in 3party lib you can add a template to do this.

#include <iostream>

using namespace std;

struct X {};

struct O {
    O(X) {
        ;
    }

    O() = default;
    O(O const&) = default;
    O(O&&) = default;
};

struct S {
    void f(const X) const {
        cout << "X" << endl;
    }
    void f(O) {
        cout << "O" << endl;
    }
};

#include <cstdlib>

template<typename TA, typename TB>
void myCall(TA a, TB b) {
    ((const TA &&)a).f(b);
}

template<>
void myCall(S a, O b) {
    a.f(b);
}

template<>
void myCall(S a, X b) {
    ((const S)a).f(b);
}

int main() {
    S s;
    myCall(s, X());
    //s.f(X());
    return EXIT_SUCCESS;
}

Upvotes: 1

Mark B
Mark B

Reputation: 96241

Why is it so?: The reason here is because the const-ness of the s object itself is also considered in overload resolution. Since s is non-const it would require adding const to the implicit this pointer to call the const f. Calling the non-const f is an exact match for the this pointer but requires an implicit conversion from X to O via O's converting constructor.

Upvotes: 2

Related Questions