Operator overload for derived classes - c++

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).

Related

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;
}

copy and swap idiom with pure virtual class

I am trying to implement virtual class with pure virtual method and 'copy and swap' idiom, but I've encountered some problems. Code won't compile because I am creating instance in the assign operator of the class A which contains pure virtual method.
Is there a way how to use pure virtual method and copy and swap idiom?
class A
{
public:
A( string name) :
m_name(name) { m_type = ""; }
A( const A & rec) :
m_name(rec.m_name), m_type(rec.m_type) {}
friend void swap(A & lhs, A & rhs)
{
std::swap(lhs.m_name, rhs.m_name);
std::swap(lhs.m_type, rhs.m_type);
}
A & operator=( const A & rhs)
{
A tmp(rhs);
swap(*this, tmp);
return *this;
}
friend ostream & operator<<( ostream & os,A & x)
{
x.print(os);
return os;
}
protected:
virtual void print(ostream & os) =0;
string m_type;
string m_name;
};
class B : A
{
public:
B(string name, int att) :
A(name),
m_att(att)
{
m_type="B";
}
B( const B & rec) :
A(rec),
m_att(rec.m_att) {}
friend void swap(B & lhs, B & rhs)
{
std::swap(lhs.m_att, rhs.m_att);
}
B & operator=( const B & rec)
{
B tmp(rec) ;
swap(*this, tmp);
return *this;
}
private:
virtual void print(ostream & os);
int m_att;
};
Error message:
In member function ‘A& A::operator=(const A&)’:|
error: cannot declare variable ‘tmp’ to be of abstract type ‘A’|
because the following virtual functions are pure within ‘A’:|
virtual void A::print(std::ostream&)|
As your compiler informs you, you cannot create a variable of abstract type. There is no way of dancing around that.
This leaves you three main options:
Stop using pure virtual functions
First, you could just get rid of the pure virtual methods and provide a little stub in each of them that calls std::terminate, which would obviously break compile time detection of whether all (former) pure virtual methods are overridden in all derived classes.
This will cause slicing, since it will only copy the base class and everything that makes out the derived class is lost.
Use a stub class w/o pure virtual functions
Similar to that, you could create a derived class that implements all virtual methods with simple stubs (possibly calling std::terminate), and is used only used as a "instantiatable version of the base class".
The most important part to implement for this class would be a constructor that takes a const reference to the base class, so you can just use it instead of copying the base class. This example also adds a move constructor, because I am a performance fetishist.
This causes the same slicing problem as the first option. This may be your intended result, based on what you are doing.
struct InstantiatableA : public A {
InstantiatableA(A const& rhs) : A(rhs) { }
InstantiatableA(A&& rhs) : A(::std::move(rhs)) { }
void print(ostream&) override { ::std::terminate(); }
};
A& A::operator=(InstantiatableA rhs) {
using ::std::swap;
swap(*this, rhs);
return *this;
}
Note: This is really a variable of type A, although I said it could not be done. The only thing you have to be aware is that the variable of type A lives inside a variable of type InstantiatableA!
Use a copy factory
Finally, you can add a virtual A* copy() = 0; to the base class. Your derived class B will then have to implement it as A* copy() override { return new B(*this); }. The reason dynamic memory is necessary is because your derived types may require arbitrarily more memory than your base class.
You're just facing the fact that inheritance works awkwardly with copy semantics.
For instance, imagine you found a trick to pass the compiling phase, what would mean (following example uses assignment but the issue is the same with a copy) :
// class A
// a class B : public A
// another class C : public A inheriting publicly from A
// another class D : public B inheriting publicly from B
B b1;
C c1;
D d1;
// Which semantic for following valid construction when copy/assignment is defined in A ?
b1 = c1;
b1 = d1;
A &ra = b1;
B b2;
// Which semantic for following valid construction when copy/assignment is defined in A ?
ra = b2;
ra = c1;
ra = d1;
CRTP is a choice:
template<typename swappable>
struct copy_swap_crtp{
auto& operator=(copy_swap_crtp const& rhs){
if (this==std::addressof(tmp))
return *this;
swappable tmp{rhs.self()};
self().swap(tmp);
return *this;
};
auto& operator=(copy_swap_crtp && rhs){
self().swap(rhs.self());
return *this;
};
protected:
auto& self(){return *static_cast<swappable*>(this);};
//auto& self()const{return *static_cast<swappable const*>(this);};
};
user class:
struct copy_swap_class
: copy_swap_crtp<copy_swap_class>
{
copy_swap_class(copy_swap_class const&);
void swap(copy_swap_class&);
};
cheers,
FM.
The compiler is right. The class A is abstract class, therefore you can not create instances of it in the operator=.
In B, you just declared the print function, but you didn't implement it. Meaning, you will get linking errors.
By implementing it, it compiles fine (if we ignore various warnings) :
void B::print(ostream & os )
{
os << m_att;
}
by the way :
B inherits privately from A, is that what you wanted?
order of initialization in A's copy constructor is wrong
you initialized m_type in A's constructor's body and not in the initialization list

What is the proper approach to swap and copy idiom in virtual inheritance?

Consider classic virtual inheritance diamond hierarchy. I wonder to know what is the right implementation of copy and swap idiom in such hierarchy.
The example is a little artificial - and it is not very smart - as it would play good with default copy semantic for A,B,D classes. But just to illustrate the problem - please forget about the example weaknesses and provide the solution.
So I have class D derived from 2 base classes (B<1>,B<2>) - each of B classes inherits virtually from A class. Each class has non trivial copy semantics with using of copy and swap idiom. The most derived D class has problem with using this idiom. When it calls B<1> and B<2> swap methods - it swaps virtual base class members twice - so A subobject remains unchanged!!!
A:
class A {
public:
A(const char* s) : s(s) {}
A(const A& o) : s(o.s) {}
A& operator = (A o)
{
swap(o);
return *this;
}
virtual ~A() {}
void swap(A& o)
{
s.swap(o.s);
}
friend std::ostream& operator << (std::ostream& os, const A& a) { return os << a.s; }
private:
S s;
};
B
template <int N>
class B : public virtual A {
public:
B(const char* sA, const char* s) : A(sA), s(s) {}
B(const B& o) : A(o), s(o.s) {}
B& operator = (B o)
{
swap(o);
return *this;
}
virtual ~B() {}
void swap(B& o)
{
A::swap(o);
s.swap(o.s);
}
friend std::ostream& operator << (std::ostream& os, const B& b)
{ return os << (const A&)b << ',' << b.s; }
private:
S s;
};
D:
class D : public B<1>, public B<2> {
public:
D(const char* sA, const char* sB1, const char* sB2, const char* s)
: A(sA), B<1>(sA, sB1), B<2>(sA, sB2), s(s)
{}
D(const D& o) : A(o), B<1>(o), B<2>(o), s(o.s) {}
D& operator = (D o)
{
swap(o);
return *this;
}
virtual ~D() {}
void swap(D& o)
{
B<1>::swap(o); // calls A::swap(o); A::s changed to o.s
B<2>::swap(o); // calls A::swap(o); A::s returned to original value...
s.swap(o.s);
}
friend std::ostream& operator << (std::ostream& os, const D& d)
{
// prints A::s twice...
return os
<< (const B<1>&)d << ','
<< (const B<2>&)d << ','
<< d.s;
}
private:
S s;
};
S is just a class storing string.
When doing copy you will see A::s remains unchanged:
int main() {
D x("ax", "b1x", "b2x", "x");
D y("ay", "b1y", "b2y", "y");
std::cout << x << "\n" << y << "\n";
x = y;
std::cout << x << "\n" << y << "\n";
}
And the result is:
ax,b1x,ax,b2x,x
ay,b1y,ay,b2y,y
ax,b1y,ax,b2y,y
ay,b1y,ay,b2y,y
Probably adding B<N>::swapOnlyMewould resolve the problem:
void B<N>::swapOnlyMe(B<N>& b) { std::swap(s, b.s); }
void D::swap(D& d) { A::swap(d); B<1>::swapOnlyMe((B<1>&)d); B<2>::swapOnlyMe((B<2>&)d); ... }
But what when B inherits privately from A?
Here's a philosophical rant:
I don't think virtual inheritance can or should be private. The entire point of a virtual base is that the most derived class owns the virtual base, and not the intermediate classes. Thus no intermediate class should be permitted to "hog" the virtual base.
Let me repeat the point: The most derived class owns the virtual base. This is evident in constructor initializers:
D::D() : A(), B(), C() { }
// ^^^^
// D calls the virtual base constructor!
In the same sense, all other operations in D should be immediately responsible for A. Thus we are naturally led to writing the derived swap function like this:
void D::swap(D & rhs)
{
A::swap(rhs); // D calls this directly!
B::swap(rhs);
C::swap(rhs);
// swap members
}
Putting all this together, we're left with only one possible conclusion: You have to write the swap functions of the intermediate classes without swapping the base:
void B::swap(B & rhs)
{
// swap members only!
}
void C::swap(C & rhs)
{
// swap members only!
}
Now you ask, "what if someone else wants to derive from D? Now we see the reason for Scott Meyer's advice to always make non-leaf classes abstract: Following that advice, you only implement the final swap function which calls the virtual base swap in the concrete, leaf classes.
Update: Here's something only tangentially related: Virtual swapping. We continue to assume that all non-leaf classes are abstract. First off, we put the following "virtual swap function" into every base class (virtual or not):
struct A
{
virtual void vswap(A &) = 0;
// ...
};
Usage of this function is of course reserved only to identical types. This is safeguarded by an implicit exception:
struct D : /* inherit */
{
virtual void vswap(A & rhs) { swap(dynamic_cast<D &>(rhs)); }
// rest as before
};
The overall utility of this is limited, but it does allow us swap to objects polymorphically if we happen to know that they're the same:
std::unique_ptr<A> p1 = make_unique<D>(), p2 = make_unique<D>();
p1->vswap(*p2);
Virtual base generally means that most derived class of object is in control of it.
First solution: Reorganize your classes to be more fit for polymorphism. Make copy construction protected. Remove assignment and swap(). Add virtual clone(). Idea is that the classes should be treated as polymorphic. So they should be used with pointer or smart pointer. Swapped or assigned should be the pointer values, not the object values. In such context swap and assignment only confuse.
Second solution: Make B and C abstract and their pointers not to manage object lifetime. Destructors of B and C should be protected and non-virtual. B and C will not therefore be most derived classes of object. Make B::swap() and C::swap() protected and not swap the A subobject, may rename or add comment that it is business of inherited classes now. That removes lot of object slicing possibilities. Make D::swap() to swap A subobject. You get one swap of A.
Third solution: Make D::swap() to swap A subobject. That way the A subobject will be swapped 3 times and lands in correct place. Inefficient? The whole construct is probably bad idea anyway. I am for example not sure how well the virtual destructors and swaps cooperate here and lot of ways to slice objects are public here. It all seems similar to attempt of making virtual assignment operators that is bad idea in C++.
If something inherits from D on its order then it should make sure by swapping or not swapping A subobject that the count of swapping A is odd. It becomes to control so should take over and fix.
The private virtual idiom is one of ways to make a class final in C++. Nothing should be able to inherit from it. Interesting that you asked. If you ever use it, be sure to comment, it confuses most of the readers of code.

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

c++ problem with function overloading

i have a problem with function overloading. I will show you with some simple example:
class A {};
class B : public A{};
void somefunction(A&, A&);
void somefunction(B&, B&);
void someotherfunction() {
...
A& a1 = ...
A& a2 = ...
...
}
Both a1 and a2 are instances of B but
somefunction(a1,a2);
calls
void somefunction(A&, A&);
What did i do wrong? I mean polymorphism and overloading are for stuff like that, arent they?
edit: Ok now i know it does not work (thanks for your answers).
Any solution how to do this? Without casting.
edit2: Ok left it as it is, with type casting, since something i would like to have is not possible. Thanks all for your help.
Cast them statically so that the compiler knows which one to pick:
void somefunction((B&)a1, (B&)a2);
The reason why you are having this problem is with the program design, not the language. Compiler picks which which function is used based on the types that are passed in. C# will behave in exactly the same way (pretty sure Java will too).
It seems to me that you are implementing polymorphism in the wrong place. somefunction really belongs inside class a and should be virtual. Then whenever it's called on the instance of a at runtime the override in the right class will be called.
So, really it should be something like this:
class a {
public:
virtual somefunction(a& a2) {
//do stuff
}
}
class b : public a {
virtual somefunction(a& a2) {
b& b2 = (b&)a2;
//do stuff
}
}
class c : public b {
virtual somefunction(a& a2) {
c& c2 = (c&)a2;
//do stuff
}
}
The above solution uses minimal casting inside the virtual function and assumes that the two instance of the same type. This means that b.somefunction(a()) will have undefined behaviour.
A better solution is to rely on C++ RTTI and use dynamic_cast, which will return NULL if the downcast is not possible.
This problem is known as double dispatch problem and is described in the wikipedia article pretty much as you described it. Furthermore, the only solution that wikipedia gives for multiple dispatch is to use dynamic_cast.
EDIT OK, this has been bugging me, here is the solution for full double dispatch between a base class and two subclasses. It aint pretty and uses a bit of C++ trickery like friend classes (for better encapsulation actually, rather than the reverse) and forward declarations.
class b;
class c;
class a {
protected:
virtual void somefunction(a& a2); //do stuff here
virtual void somefunction(b& b2); //delegate to b
virtual void somefunction(c& c2); //delegate to c
public:
virtual void doFunc(a& a2) {
a2.somefunction(*this);
}
friend class b;
friend class c;
};
class b : public a {
protected:
virtual void somefunction(a& a2); //do stuff here
virtual void somefunction(b& b2); //do stuff here
virtual void somefunction(c& c2); //delegate to c
public:
virtual void doFunc(a& a2) {
a2.somefunction(*this);
}
friend class a;
};
class c : public b {
protected:
virtual void somefunction(a& a2); //do stuff here
virtual void somefunction(b& b2); //do stuff here
virtual void somefunction(c& c2); //delegate to c
public:
virtual void doFunc(a& a2) {
a2.somefunction(*this);
}
friend class a;
friend class b;
};
//class a
void a::somefunction(a& a2) {
printf("Doing a<->a");
}
void a::somefunction(b& b2) {
b2.somefunction(*this);
}
void a::somefunction(c& c2) {
c2.somefunction(*this);
}
//class b
void b::somefunction(a& a2) {
printf("Doing b<->a");
}
void b::somefunction(b& b2) {
printf("Doing b<->b");
}
void b::somefunction(c& c2) {
c2.somefunction(*this);
}
//class c
void c::somefunction(a& a2) {
printf("Doing c<->a");
}
void c::somefunction(b& b2) {
printf("Doing c<->b");
}
void c::somefunction(c& c2) {
printf("Doing c<->c");
}
The function to call is only determined at run-time for virtual methods, based on the type of the this object:
A* a = new B;
a->foo(); //calls B::foo (as long as foo is virtual)
The function to call is not resolved at run-time based on the "real" type of a function's arguments.
A* a = new B;
X* x = new Y;
a->foo(x); //assuming virtual and two overloads, calls B::foo(X*), not B::foo(Y*)
There is no built-in double dispatch mechanism (to select the function to call based on the dynamic types of two objects at the same time), although the pattern can be manually implemented as some posts show.
If you say that you always know that the A& will actually be B& and don't want casts, I conclude that the types will be hard-coded known at compile-time, so you might try "compile-time polymorphism" instead. (In this case A and B don't even need to be related, as long as they have a suitable interface.)
class A {};
class B {};
class C: public A {};
void somefunction(const A&, const A&);
void somefunction(const B&, const B&);
template <class T>
void someotherfunction()
{
const T& a1 = T();
const T& a2 = T();
somefunction(a1, a2);
}
int main()
{
someotherfunction<A>();
someotherfunction<B>();
//combine with inheritance and it will still be
//possible to call somefunction(A&, A&) since
//somefunction(C&, C&) is not defined
someotherfunction<C>();
}
Now a1 and a2 will really be As in one instantiation and Bs in the other case, as far as selecting the overload is concerned. (I added some consts, because otherwise it would be harder to produce something that binds to non-const references.)
As others have already mentioned, the compiler picks the correct overload - its how the language works.
If you are really sure of what type the instances are, you should just cast. If not, one way you can get around manual type-checking at run-time is double dispatch:
struct A;
struct B;
struct Base {
virtual perform(Base& b) = 0;
virtual perform(A& a) = 0;
virtual perform(B& b) = 0;
};
struct A : Base {
virtual perform(Base& b) { b.perform(*this); }
virtual perform(A& a) { someFunction(a, *this); }
virtual perform(B& b) { someFunction(b, *this); }
};
struct B : A {
virtual perform(Base& b) { b.perform(*this); }
virtual perform(A& a) { someFunction(a, *this); }
virtual perform(B& b) { someFunction(b, *this); }
};
// ...
Base& b1 = foo1();
Base& b2 = foo2();
b1.perform(b2);
what exactly are you trying to do? it looks like you are trying to write a function that does something given two objects, and you want it to do a different thing based on the type of the combination of objects?
remember that even normal polymorphism does "checks" internally.
this is an interesting problem tho,
polymorphism gives you the ability to easily overload the functionality of a function based on the type of ONE object, not two.
what is it EXACTLY that you are trying to do? my best suggestion would be to make it so that each object could perform its own specific stuff separately and then return a common object for common processing:
class Base
{
virtual SomeComonInterfaceObject DoMySpecialSomething() = 0;
}
void _doSomething(SomeComonInterfaceObject a, SomeComonInterfaceObject b);
void doSomething(Base& o1, Base& o2)
{
_doSomething(o1->DoMySpecialSomething(), o2->DoMySpecialSomething());
}
if that doesn't suit, you probably just have to check the type and do specifics based on that.
note that even normal polymorphism does "checks" if you are worried about performance, any other language would have to too.
the only way you might be able to get around that is by using templates, and it would probably get real ugly.
would be interesting to know what you are trying to do. also, these doSomething functions, is it always the case that their two parameters are the same type? or do they mix and match?
Yes but C++ decided which function to use at compile time, not at runtime. And at compile time the only thing the compiler sees is (A&, A&) - it cannot know that those are actually instances of B.
You should post more code....what is
A& a1 = ...
A& a2 = ...
Shouldn't you use pointers?
If you're storing a1 and a2 as type A then even if they are also B's the A overload gets called. You'd have to dynamic_cast them.
In this case compiler will always call somefunction(A&, A&);. Why would it call somefunction(B&, B&);?
You said in a comment that you're SURE that they are B's.
If that is the case then this is what you want to do.
B a1();
B a2();
If you ever need A's, you can do this (A*)&B. That is an implicit cast and I'm pretty sure it happens at compile time.
Your compiler has chosen what it thinks is the most appropriate overload. a1 and a2 are both declared as references to class A, so they fit the overload which takes references to class A "better" than they fit the other one, since that would require some sort of implicit cast to convert them to class B.
Note also that you can't implicitly upcast that way. If you have a pointer or reference to an instance of the base class (A in this case) then it can't be implicitly converted to a derived class, because in general not all instances of the base class are instances of the derived class (all Bs are As, but not all As are Bs).
You will need to declare them as instances of B before calling the function:
B& b1 = ...
B& b2 = ...
somefunction(b1, b2);
What i will do is to use a dispatch table to get what i want. Instead of 1 dimensional it may be 2 or 3 dimensional (probably 2). Thanks all for trying to help me!