Interconvertible types and ambiguous calls - c++

I have a sequence of types, which I want to be freely convertible to one another. Consider the following toy example:
struct A {
int value;
A(int v) : value(v) { }
};
struct B {
int value;
B(int v) : value(v) { }
B(A a) : value(a.value) { }
operator A() const { return A(value); }
};
struct C {
int value;
C(int v) : value(v) { }
C(A a) : value(a.value) { }
C(B b) : value(b.value) { }
operator B() const { return B(value); }
operator A() const { return A(B(*this)); } // <-- ambiguous
};
int main(int argc, const char** argv) {
C c(5);
A a(3);
a = c;
}
So as you see, I'm trying to defined each subsequent type to be convertible from all previous types using cast constructors, and to be convertible to all previous types using cast operators. Alas, this does not work as intended, as the definition of C::operator A is ambiguous according to gcc 4.7:
In member function ‘C::operator A() const’:
19:40: error: call of overloaded ‘B(const C&)’ is ambiguous
19:40: note: candidates are:
9:3: note: B::B(A)
6:8: note: constexpr B::B(const B&)
6:8: note: constexpr B::B(B&&)
Changing the expression to static_cast<A>(static_cast<B>(*this)) doesn't change a thing. Removing that line altogether results in an error message in main, as no implicit conversion sequence may use more than one user-defined conversion. In my toy example, I could perform the conversion from C to A direcly, but in my real life application, doing so would cause a lot of duplicate code, so I'd really like a solution which reuses the other conversion operators.
So how can I obtain a set of three freely interconvertible types without duplicating conversion code?

I'd try this way in struct C:
operator A() const { return this->operator B(); }

Try this:
operator A() const { return A(B(value)); }
or this:
operator A() const { return A(operator B()); }

Related

User defined conversion assigned to const ref variable via temporary object

The code below is a simplified version of the actual problem I am facing.
Assume I do not have permission to modify class A (as it is external library), and its already widely used in my existing code base.
The const & assignment from a temporary object (direct constructor) which also return a const & member variable via implicit conversion is not valid in this case.
How do I prevent or make it legal in this case so that the caller gets the correct A value?
class A
{
public:
A() { }
A(int _r, int _g, int _b)
: r(_r), g(_g), b(_b)
{
}
~A(){ }
int GetR() const { return r; }
int GetG() const { return g; }
int GetB() const { return b; }
private:
int r = 0;
int g = 0;
int b = 0;
};
class Foo
{
public:
Foo() : Foo(A()) {}
Foo(int _r, int _g, int _b) : a(A(_r, _g, _b)) {}
explicit Foo(const A& _a) : a(_a) {}
Foo& operator=(const A& a)
{
*this = Foo(a);
return *this;
}
operator A() const { return a; }
operator const A&() const { return a; }
private:
A a;
};
int main()
{
const A& a = Foo(200, 100, 300);
std::cout << a.GetR() << a.GetG() << a.GetB() << endl; // I may not get 200 100 300 here as Foo is already out of scope
return 0;
}
Motivation
Some background on why I am implementing a class as above. The actual purpose of class Foo is to contain 2 different objects, which actually has the same purpose, just different way of storing data internally. For example, let's say class A and class B, which stores RGB value of color in int and floating (normalized) respectively. And as mentioned above, I do not have permission to modify class A, and its already widely used in my code base.
There are tons of function in my code base which takes in const A& and const B& as a function param. So I am trying to unify this 2 classes for a particular case, where I can just pass in Foo in those places and it will work as expected.
You can apply ref-qualified member functions (since C++11), i.e. mark the conversion operator with lvalue-reference, to prevent it being called on temporaries (rvalues).
class Foo
{
public:
... ...
operator A() const { return a; }
operator const A&() const & { return a; }
operator const A&() && = delete;
... ...
};
Then
const A& a = Foo(200, 100, 300); // invalid; invokes deleted operator
const A& a = static_cast<A>(Foo(200, 100, 300)); // fine; invokes operator A()

Deleting all lvalue conversion operators

I want to avoid all lvalue conversions from a type to another type:
struct A
{};
struct T
{
A a;
operator A() { return a; }
//operator A&() = delete; how to delete lvalue conversions to A?
};
void bar(A)
{}
void foo(const A&)
{}
void foo2(A&)
{}
int main()
{
T t;
bar(t); // fine
foo(t); // should never convert to ref-to-const A
foo2(t); // should never convert to ref-to A
return 0;
}
Is this possible?
How and which conversion operators do I need to delete?
Example on godbolt
You might do
struct T
{
A a;
operator A() { return a; }
template <typename T> operator const T&() = delete;
};
Demo

C++ compilation error: cannot convert from B to A, no constructor, or constructor overload ambiguity

I have some code which implies a type conversion, which does not compile although there is a conversion method...
class A
{
public:
A(void) :_m(0) { }
A(int val) : _m(val) {}
private:
int _m;
};
class B
{
public:
B(void) : _m(0) {}
B(int val) : _m(val) {}
B(const A&);
// there is a direct conversion operator here
operator A(void) const { return A(_m); }
operator int(void) const { return _m; }
private:
int _m;
};
int main()
{
B b;
A a = (A)b; // error C2440 here
}
Here is the error message:
error C2440: 'type cast': cannot convert from 'B' to 'A'
message : No constructor could take the source type, or constructor overload resolution was ambiguous
The error message means that these two operators
operator A(void) const { return A(_m); }
operator int(void) const { return _m; }
can be used in the expression
(A)b;
As a result using these conversion operators there can be used either the constructor A( int ) or the default copy constructor A( const A & ).
To make it more clear rewrite the corresponding declaration like
A a = A( b );
So whether the object b is converted to an object of the type A using the first conversion operator or to an object of the type int using the second conversion operator.
You could avoid the ambiguity declaring the operators for example like
operator A(void) const & { return A(_m); }
operator int(void) const && { return _m; }
that is for lvalues the first operator will be used and for rvalues the second operator will be used.
Here is your program with the modified operators.
#include <iostream>
class A
{
public:
A(void) :_m(0) { }
A(int val) : _m(val) {}
private:
int _m;
};
class B
{
public:
B(void) : _m(0) {}
B(int val) : _m(val) {}
B(const A&);
// there is a direct conversion operator here
operator A(void) const & { return A(_m); }
operator int(void) const && { return _m; }
private:
int _m;
};
int main()
{
B b;
A a = b;
A a1 = B();
}
From what I understand, the compiler tries several paths to interpret a = (A)b.
it finds the operator A
but it also finds the operator int on B, and the A(int) constructor which gives it a second path B => int => A...
And it does not know which to pick.
To fix the compilation, I can:
remove the operator int from B
rewrite the error line as A a = b.operator A();...

Implicit conversions in C++

Given the following code, why doesn't the compiler resolve the implicit conversion when constructing Bar? That is, construct Foo just like a was constructed which is (should) then be used to construct Bar?
#include <string>
class ImplicitlyConvertToChar
{
public:
ImplicitlyConvertToChar(const char* a_char)
: m_string(a_char)
{ }
ImplicitlyConvertToChar(const char* a_char, size_t a_end)
: m_string(a_char)
{
}
template <typename T_String>
ImplicitlyConvertToChar(T_String const& a_string)
: m_string(a_string.begin())
{
}
operator char const * () const
{ return m_string; }
const char* m_string;
};
class Foo
{
public:
Foo(const ImplicitlyConvertToChar& a_charLike)
: m_string(a_charLike)
{ }
const char* m_string;
};
class Bar
{
public:
Bar(const Foo& a_foo)
: m_foo(a_foo)
{ }
Foo m_foo;
};
int main()
{
Foo a("this works");
Bar b("Why doesn't this?");
}
You are not allowed more than one user defined implicit conversion. The Foo example involves one, the Bar example involves two.
The compiler is only allowed to make a single implicit user-defined conversion.
See http://en.cppreference.com/w/cpp/language/implicit_cast
A user-defined conversion consists of:
zero or one non-explicit single-argument constructor or non-explicit
conversion function calls
Constructing Bar that way would require two.

Nested implicit conversion in C++

I wanted have implicit conversion in two level. The following code snippet is prototype of the problem I am facing.
//Sources
class A
{
public:
void print()
{
std::cout <<"Class A"<< std::endl;
}
operator int()
{
return 1;
}
};
class B
{
public:
void print()
{
std::cout <<"Class B"<< std::endl;
}
operator A()
{
return A();
}
};
class C
{
public:
void print()
{
std::cout <<"Class C"<< std::endl;
}
operator B()
{
return B();
}
};
void print_(A a)
{
a.print();
}
//driver
int main( int argc, char* argv[] )
{
C c;
//print_( c ); // compilation error
//print_( C() ); // compilation error
print_( c.operator framework::configuration::B() ); //when explicitly invoked it worked
return 0;
}
I looked into the example provided in the following links was convinced this is achievable.
How do conversion operators work in C++?
Operator overloading
The standard only allows one implicit conversion involving a user defined type. You have two, hence the compilation error.
See 12.3/4
At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single
value.