Specify priority between implict conversions - c++

I am trying to find a way to specify a hierarchy of implicit conversions between different types.
Suppose I have two types and a function overload for each:
struct A{};
struct B{};
void f(A const& a){}
void f(B const& b){}
Now I have another class that it is implicitly convertible to both A and B
class C{
A to_A() const{return A{} ;}
B to_B() const{return B{};}
operator A() const{return to_A() ;}
operator B() const{return to_B();}
};
As it is, I cannot use f directly because of ambiguity.
int main(){
C c;
f(c); // ambiguous, convert c to a, o c to b??
}
Let's also say that a conversion to B is more sensible given the option.
Is there way to specify in struct C one of the two (A or B) as the preferred conversion?, so that main compiles and is equivalent to f(c.to_B());
Full code here: https://godbolt.org/z/xvz71qEhK
Possible near solutions I discarded:
One obvious way is to make one conversions (to A) explicit, but I want that both conversion are implicit. Because there are other functions (not overloaded like f) where both conversion can be implicit.
At the place of call make the conversion explicit, main(){f(c.to_B());} The problem is that I am using this in generic template functions where the template parameter can be A, B or C.
For every ambiguous overload make a new overload f(C const& c){return f(c.to_B());}, the problem is that I have to do this for every overloaded functions that can take A or B and I have many of them. I can have tens of f-like functions, some of them with more than one argument (exploding number of combinations).
What I tried so far: I though that defining a hierarchy would help to select the preferred conversion, and that the operators of the leaf class would have preference, but it didn't remove de ambiguity.
https://godbolt.org/z/fYj7PP5GE
template<class CRTP>
struct base_to_A{
operator A() const{
return static_cast<CRTP const&>(*this).to_A() ;
}
};
class C : public base_to_A<C>{
A to_A() const{return A{};}
B to_B() const{return B{};}
public:
operator B() const{return to_B();}
};
Playing with public/protected/private didn't help.

Use variants in the API, and dispatch to template implementations if needed.
Have a conversion function that knows which you prefer.
void f(std::variant<A,B,C> var1, std::variant<A,B,C> var2){
return std::visit[&](auto& v1, auto& v2){
f_impl(v1,v2);
}, prefer_variant_convert<A,B>(var1), prefer_variant_convert<A,B>(var2) );
}
we take in variants, we run code to convert them to preferred variant types in order, then we generate exponential amounts of code to unnpack the variants.
Or, template version:
void f(auto var1, auto var2){
return f_impl( prefer_convert<A,B>(var1), prefer_convert<A,B>(var2) );
}
the prefer_convert<Ts...>(T0) returns T0 if it is in Ts..., otherwise the first of the Ts that T0 can convert to. It shouldn't be hard to write.

This doesn't answer the question in general, it is only in particular case and considering a certain design.
A partial solution that works in some designs:
If C can be implemented in terms of B alone and publicly, the conversion (actually a cast to B) will be preferred:
#include <cstdio>
struct A{};
struct B{};
void f(A const& a){std::puts("A");}
void f(B const& b){std::puts("B");}
class C : public B{
A to_A() const{return A{} ;}
// B to_B() const{return *this;} // not used
public:
operator A() const{return to_A() ;}
// operator B() const{return to_B();} // not needed, will never be called anyway
};
int main(){
C c;
f(c); // preferres B
}
https://godbolt.org/z/nnMPGbjeM

Related

C++ template subclass and multiple inheritance ambiguity

I have two base classes A, and B, and a third class C that (virtually) derives from both of them. Each class exposes its own public shared_ptr type.
In another class I have two vectors where I want to add objects of type A to one vector, objects of type B to another vector, and objects of type C to both. This results in three add methods, one for each of those three classes.
My problems arise when I try to further derive from C:
#include <iostream>
#include <memory>
#include <vector>
class A {
public:
using shared_ptr = std::shared_ptr<A>;
virtual ~A() {};
};
class B {
public:
using shared_ptr = std::shared_ptr<B>;
virtual ~B() {};
};
class C : virtual public A, virtual public B {
public:
using shared_ptr = std::shared_ptr<C>;
virtual ~C() {};
};
class D : virtual public C {
public:
virtual ~D() {};
};
class Test {
protected:
std::vector<A::shared_ptr> vecA;
std::vector<B::shared_ptr> vecB;
public:
void add(const A::shared_ptr& o) {
std::cerr << "in A" << std::endl;
vecA.push_back(o);
}
void add(const B::shared_ptr& o) {
std::cerr << "in B" << std::endl;
vecB.push_back(o);
}
void add(const C::shared_ptr& o) {
std::cerr << "in C" << std::endl;
vecA.push_back(o);
vecB.push_back(o);
}
};
int main()
{
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
auto c = std::make_shared<C>();
auto d = std::make_shared<D>();
Test t;
t.add(a);
t.add(b);
t.add(c);
t.add(d);
}
This doesn't work - the resolution of which version of add to call cannot be determined:
test.cc:62:7: error: call to member function 'add' is ambiguous
t.add(d);
~~^~~
test.cc:34:10: note: candidate function
void add(const A::shared_ptr& o) {
^
test.cc:39:10: note: candidate function
void add(const B::shared_ptr& o) {
^
test.cc:44:10: note: candidate function
void add(const C::shared_ptr& o) {
^
I do have the option of simply passing my C object separately to both Test::add(const A::shared_ptr&) and Test::add(const B::shared_ptr&) because in reality the B version of add has additional parameters that resolve the overload but I would prefer that the caller not have to remember to do this.
Is this ambiguity resolvable? My target environment constrains me to C++14.
The standard derived-to-base conversion sequences take the length of the inheritance chain into account when ranking conversion sequences, a close base would be deemed a better conversion sequence than a one that is further up the inheritance chain. And that in turn affects pointers and references too!
Sadly, since smart pointers are user defined types, they cannot benefit from this behavior. All three overloads are viable via a (valid) user defined conversion. And the "ranks" of the individual bases don't affect the ranking of the overloads.
But that doesn't mean we can't re-introduce the ranking impose by a derived-to-base conversion. We just need to do so via another argument. And by employing tag-dispatch, we can do just that.
We can define a helper utility type:
template<int n> struct rank : rank<n - 1> {};
template<> struct rank<0> {};
For any 0 <= i < j <= k, the conversion sequence of rank<k> -> rank<j> will always be deemed better than rank<k> -> rank<i>. So, if we make your overload set inaccessible, and rank them explicitly:
protected:
void add(const A::shared_ptr& o, rank<0>) { /*...*/ }
void add(const B::shared_ptr& o, rank<0>) { /*...*/ }
void add(const C::shared_ptr& o, rank<1>) { /*...*/ }
We can then expose another overload in the form of a function template:
public:
template<typename T>
void add(const std::shared_ptr<T>& o) {
return add(o, rank<10>{});
}
It mainly just forwards to one of the protected overloads, but it adds another argument. A rank tag. This will affect overload resolution too. Even though all three add overloads are viable, the derived-to-base conversion of rank<10> will affect the choice of best one.
Here it is live.

Virtual-like friend functions?

I want to create interface like
class Scalar {
public:
Scalar() {}
virtual ~Scalar() {}
//virtual members operators
virtual Scalar& operator+() const = 0;
virtual const Scalar operator-() const;
virtual Scalar& operator=() = 0;
virtual Scalar& operator+=() = 0;
//...
};
I intend also to use some friend functions, for example:
friend const Scalar operator+(const Scalar&, const Scalar&);
But there is a problem when I derive the abstract class, and create derived class, say:
class RealNumber: public Scalar {
public:
friend const RealNumber operator+(const RealNumber&, const RealNumber&);
//some definitions...
};
According to this logic, I would need to define a new overload of friend operator+ for every new class derived from Scalar. Is there some way to solve this problem and avoid declaring these friends in all the derived classes?
Is this your problem ?
I understand that your problem is that your two friends refer to totally different functions, since they have a different signature:
friend const Scalar operator+(const Scalar&, const Scalar&);
friend const RealNumber operator+(const RealNumber&, const RealNumber&);
Worse, the choice of a class external friend will not be polymorphic: the right friend will be chose based on the compile-time type.
How to solve it ?
First of all, instead of using an outside overloaded friend, you could consider overriding the operator of the class itself (keeping the signature identical).
However this has two major challenges:
it is almost impossible to return a reference from arithmetic operator, unless you'd accept side-effects, which would then make your operator behave differently than expected.
you'd need to cope with combining different kind of scalars: what if you'd have to add a Scalar to a RealNumber ? This would require a double dispatch to be implemented to cope with all the possible combination.
So, dead-end ?
No, there are two other alternatives, depending on your real problem:
Do you really want to combine arithmetic type dynamically at run-time ? If yes, you need to go away from the C++ operator overriding approach, and implement an expression evaluator, using the interpreter pattern.
If not, consider to use a template based design, so that the compiler choses at compile-time the appropriate specialisation.
Or suffer with the many possible friends and their combination of parameter types.
You may not be able to create virtual friend functions, but you can create virtual operators (even operator + could be done this way).
Consider the following code: WARNING: THIS IS NOT GOOD DESIGN AT ALL
#include <iostream>
using namespace std;
class AA {
private:
int a;
public:
AA(int a): a(a) {};
inline int getA() const { return a; };
virtual AA operator +(const AA &a) {
AA res(this->getA() + a.getA());
return res;
}
};
class BB: public AA {
public:
BB(int a): AA(a) {}
virtual AA operator +(const AA &a) {
AA res(this->getA() - a.getA());
return res;
}
};
int main() {
BB tmp(1);
AA& a = tmp;
AA b(7);
cout << (a + b).getA();
return 0;
}
When I was writing this code, I found that a lot of flaws could be induced (like the + operator that really does substraction instead, also what if the second operand was BB instead of the first one ??)
So, about your problem, you want Scalars. So you can do the following approach:
#include <iostream>
using namespace std;
// Here, Scalar acts as an abstract class, all its goal is to convert your
// class type into some calculable value type (e.g. you can use T as double)
template <typename T>
class Scalar {
public:
// Converter function is abstract
virtual operator T() = 0;
};
class AA: public Scalar<double> {
private:
double a;
public:
inline double getA() {return a;};
AA(double a): a(a) {}
// Implements the converter function in Scalar, T in scalar is mapped
// to double because we did Scalar<double>
virtual operator double() {
return a;
}
};
class BB: public Scalar<double> {
private:
int a;
public:
inline double getA() {return (double)a;};
BB(int a): a(a) {}
virtual operator double() {
return (double)a;
}
};
int main() {
BB tmp(1);
AA b(7);
// Here, it is easy for us just to add those, they are automatically converted into doubles, each one like how it is programmed.
cout << (b + tmp);
return 0;
}

Operator overload for derived classes

I have a base container class, which has a number of operators (=,+,-,+=,etc). It is expected that the logic of the operators will not need to be changed for the derived classes. Thus, ideally, I would like to use the base class operators for all of its derived classes without having to redefine them for each of the derived classes explicitly (with the exception of the assignment operator).
A solution that I came up with is demonstrated below based on a simple example. The solution seems to work, but I have doubts about the validity of the method for more complicated cases. Do you think it is valid to use this assignment "hack" in class B? What are the potential pitfalls of this method? Is there anything I missed? Are there easier ways of achieving the functionality that I need (i.e. using base class operators for derived classes)?
class A
{
protected:
int a;
public:
A(int ca)
{
a=ca;
}
A(const A& A1)
{
a=A1.a;
}
int geta() const
{
return a;
}
void seta(int ca)
{
a=ca;
}
const A& operator=(const A& A1)
{
a=A1.a;
return *this;
}
};
const A operator+(const A& A1, const A& A2)
{
A myA(A1.geta()+A2.geta());
return myA;
}
class B: public A
{
public:
B(int a): A(a) {}// ... using A::A;
const B& operator=(const B& B1)
{
a=B1.geta();
return *this;
}
const B& operator=(const A& B1)
{
a=B1.geta();
return *this;
}
};
int main()
{
B myB(4);
A myA(3);
//need this to work
myB=myB+myB;
cout << myB.geta();
myA=myA+myA;
cout << myA.geta();
int j;
cin >> j;
}
For the example given i don't see any problems that can happen. Of cause you can improve the code, first by returning non const reference in operator=, second i think by adding += op to your class, and using it's code inside global operator+ function.
But in general i think it should work fine. As for assignment operators - as soon as you have only POD types and no pointers, or references or handles you don't really need any.
It will become more complicated, however when pointers appear. You'll need to make sure you copy objects, pointed by them, or manage them some other way.
And you probably will not need to modify your operators as long as you don't add more members to derived classes, which also should take part in calculations.
In general you don't have to redefine functions in the base class for derived classes. With public inheritance your derived classes will have access to those functions and use their operation just fine. If you do want to re-define them (= operator for example), be sure you call the right one (virtual functions help in this case).

Error while calling constructor from another constructor in C++

I have a struct A that has several constructors with different data members initialized.
template<typename T>
struct A {
typedef std::vector<T> type1
type1 a;
type1 b;
type1 c;
A(type1 i_a): a(i_a) {
}
A(type1 i_a, type1 i_b): A(i_a), b(i_b) {
}
A(type1 i_a, type1 i_b, type1 i_c): A(i_a, i_b), c(i_c) {
}
};
Error I get is when I instantiate it with say custom_type, the error is
type A<custom_type> is not direct base of A<custom_type> highlighting the constructor I call within another constructor. I am using C++11. What's the problem?
A constructor may initialize its base classes and members, OR delegate to another constructor of the same class, not both.
A(i_a) constructs a complete A object, copying member a and default-constructing b and c. So it doesn't make sense to have A(type1 i_a, type1 i_b): A(i_a), b(i_b) {} - the first initializer has already initialized b. You could instead have
A(type1 i_a, type1 i_b) : A(i_a) { b = std::move(i_b); }
aschepler provides the answer but I wanted to explain what lead to the problem and show 'best practices' for delegating constructors to avoid it.
In your code you have a series of constructors, each more general than constructor they delegate to. That is you're trying to have a constructor delegate to a more specialized constructor that doesn't do everything and then you tack on a bit of work to handle the extra generality. This is backwards from what has turned out to be the most practical way of construction delegation in earlier languages that support it.
What you want instead is to have the most general constructor be the 'root' of the delegation (the 'designated initializer' or 'designated constructor', in other languages). More specialized constructors will do their work using more general constructors, passing them the data for the special case to be handled.
In your case the specialized behavior is to use default values for some members instead of taking initial values from the user. So your more specialized constructors will do their work by passing on the parameter they're given along with default values for the other members.
template<typename T>
struct A
{
typedef std::vector<T> type1;
type1 a;
type1 b;
type1 c;
A(type1 i_a, type1 i_b, type1 i_c): a(i_a), b(i_b), c(i_c) {}
A(type1 i_a, type1 i_b): A(i_a, i_b, {}) {}
A(type1 i_a): A(i_a, {}) {}
};
but you can still call different constructors of the same class from the body of constructor (if for some reason you would want to)
class CComplex{
public:
CComplex(int real1,int image1,char c)
{
cout<<"RealImg";
real=real1;
image=image1;
char* x; char xc=c;x=&xc;
void* v;
f(x);
CComplex ccc(x,v); //this is OK
CComplex cccc(1,2,3); //as this too
}
CComplex():real(0),image(0){cout<<"DEFAULT";}
CComplex(const CComplex &c)
{
real=c.real;
image=c.image;
cout<<"COPY";
}
CComplex& operator=(CComplex const& ref){
cout<<"ASSIGN";
//CComplex* c;
CComplex cobj(43,45,'x');
//c=&cobj;
//CComplex* c=new CComplex(44,45);
return cobj;
}
CComplex(int i1, int i2, int i3){cout<<"\n123!";}
CComplex(const char* y,void* v){cout<<"\nCrefvoid!";}
~CComplex(){cout<<"\n~CComplex(){}";}
public:
void Display(void)
{
cout<<real<<"+"<<image<<"i"<<endl;
}
static bool CComplexComparator(CComplex c1, CComplex c2){return true;}
static void CComplexInit(CComplex& c){
c.real=100;
}
int real,image;
};

Should I dynamic cast or not

My problem is this, I have a base class from which classes are derived. I have an overloaded += operator in the base class but I only want to be able to add derived classes of the same type. I do this by returning the lhs and producing a warning message if they are not the same type.
class base{
int variable;
public:
base(int v);
base & operator+=(const base & rhs);
class a: public base{
}
class b: public base{
}
base & operator+=(const base & rhs){
if(type_same(const & rhs){
variable+=rhs.variable; }
return *this
}
a first(1);
a second(5);
b third(2);
first+=second // first.variable now =6
second+=third // warning produced second.variable still = 5
I've never used dynamic cast before so my question is is it possible/advisable to a check using it and typeid to find out whether the types passed are the same. My alternative is a string constant name for each derived class.
Why have the operator+ in the base class in the first place? If you just place operator+ with the correct type into the a and b class, it will work, without any dynamic_cast.
dynamic_cast is usually a hint that your types are not well designed and should be avoided. In the described scenario, you can surely get by without any dynamic_cast. If not, then a and b should not be derived from base, as they do behave differently. Think about it from the user perspective: By deriving a from base, you tell the user that a can be used as base. If a method requires the actual type, move it down instead of putting it into the base interface.
The correct usage btw. would be to dynamic_cast the rhs to the current type (i.e. dynamic_cast<b*>(&rhs)) and if that returns a non-zero value, then rhs was indeed of the correct type (however, think about what happens if there is a class derived from b being passed in!)
To solve the problem that you stated in your post, dynamic_cast and RTTI are definitely the right tools. However, issuing a warning at runtime while providing compile-time support for a '+' operation may point to issues in the design where the users of your API would not expect exactly the behavior that your API provides.
My take: you could employ CRTP
The below code will issue the type mismatch error at compile time, as suggested by others.
template <typename Derived>
class base{
int variable;
public:
base(int v) { variable = v; }
Derived& operator+=(const Derived& rhs);
};
struct a: base<a> {
a(int v): base<a>(v) {}
};
struct b: base<b> {
b(int v): base<b>(v) {}
};
template <typename Derived>
Derived& base<Derived>::operator+=(const Derived& rhs)
{
variable += rhs.variable;
return static_cast<Derived&>(*this);
}
int main(int argc, const char *argv[])
{
a a1(1), a2(2);
b b1(1), b2(2);
a1 += a2;
b1 += a2; // compile time error :)
return 0;
}
Even if you really wanted to type the operator arguments as base& you'd at least gain the benefit of using static_cast<> because the conversion is exact and well defined at runtime. I can expand the idea in a few