c++g++llvmambiguous

Resolve ambiguity in assignment constructor c++


Description

I have code that is ambiguous when a certain constructor is present. But, when I comment said constructor out, then the compiler complains that a necessary constructor is missing.

Minimum Working Example

struct X;

struct E{
    E(const double& r){ /* important protocol stuff */ }
    E(const X&);
};

struct X{
    X(){ /* important init stuff */ }
    ~X(){ /* important delete stuff */ }
    //X(const double& r){ *this=E(r); }  // POSITION 1
    X(const X& x){ /* important init stuff */ *this=x; }
    X(const E& e){ /* important init stuff */ *this=e; }
    const X& operator=(const X& x){ return *this=E(x); }
    const X& operator=(const E& e){ /* important stuff */ return *this; }
};

E::E(const X& x){ /* important protocol stuff */ }

E operator+(const E& x, const E& y){ /* important protocol stuff */ return E(1); }
E operator*(const E& x, const E& y){ /* important protocol stuff */ return E(2); }

int main(){
    X x,y;
    x = 3.0;
    X u = 4.0;  // POSITION 2
    y = x + u * 5.0;
    X z = 6.0 + 7.0 * y;
}

With position 1 commented out, position 2 throws an error.

With position 1 included, there is an ambiguity error.

Basically, I want to remove position 1 and with that double->X be cast via double->E->X.

Questions

  1. What is the name of the problem?
  2. How do I fix it?

Things I tried:


Solution

  • The ambiguity occurs because (in the x = 3.0; line) the compiler can't decide which of the two assignment operators to use: the one with the X& argument or the one with the E&, as both parameter types are convertible from the given double (because both E and X have constructors that take a const double& parameter).

    You can resolve this error by providing a third assignment operator, which takes a const double& argument, like so:

    struct X {
        X() { /* important init stuff */ }
        ~X() { /* important delete stuff */ }
        X(const double& r){ *this=E(r); }  // Now uncommented (required)
        X(const X& x) { /* important init stuff */ *this = x; }
        X(const E& e) { /* important init stuff */ *this = e; }
        const X& operator=(const double& x) { return *this = E(x); } // Add this!
        const X& operator=(const X& x) { return *this = E(x); }
        const X& operator=(const E& e) { /* important stuff */ return *this; }
    };