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
- What is the name of the problem?
- How do I fix it?
Things I tried:
- explicit keywords in front of various constructors. For E, this results in errors after position 2. For X, this results in the same error as with position 1 commented out.
- removing constructors/operators from the definitions from X,E. This however is no solution, because I need to be able to include some important stuff.
- trying different compilers (g 8.3.0 and 9.2.0, clang 12.0.0). This did not change the issue.
CodePudding user response:
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; }
};