1

Consider the following code:

#include <iostream>

class Base{
public:
    int iB;
    Base() :iB(0){};
    virtual ~Base(){}
    virtual void foo(Base&, const int&, const int&) const = 0;
};


class Derived : public Base{
public:
    int iD;
    Derived() :iD(1){};
    void foo(Derived&, const int&, const int&) const;
};
void Derived::foo(Derived& d, const int& i1, const int& i2) const{ 
    std::cout << d.iD; 
}


class BaseWrap{
public:
    BaseWrap(){};
    virtual ~BaseWrap(){};
    virtual Base* bar() const = 0;
protected:
    Base* b;
};


class DerivedWrap : public BaseWrap{
public:
    DerivedWrap(){};
    Derived* bar() const;
private:
    Derived* d;
};
Derived* DerivedWrap::bar() const { 
    return new Derived; 
}

int main(){

    return 0;
}

This results in a compiler error "Error: object of abstract class type "Derived is not allowed" pure virtual function Base::foo" has no overrider. I assumed that thanks to polymorphism I can always put a pointer to a derived class where a pointer to Base class is expected. Anyway, I tried changing the Derived class to the below:

class Derived : public Base{
public:
    int iD;
    Derived() :iD(1){};
    void foo(Base&, const int&, const int&) const;
};
void Derived::foo(Base& d, const int& i1, const int& i2) const{
    std::cout << d.iD; 
}

However, now I I get the error "Error: class "Base" has no member "iD".

EDIT:

foo takes a ref to derived because in my real implementation i.e. I want to be able to do this:

Derived d1;
Derived d2;
d1.foo(d2, 0, 1);

Furthermore, I probably should have been more clear on what I am actually asking. I realize that removing the pure virtual function declatation

virtual void foo(Base&, const int&, const int&) const = 0;

fixes the issue. However, in all derived class implementation the code is exactly the same, and only varies in the type of the first argument (derived classes from Base). So it feels like there should be a pure virtual function to enforce existence of foo.

Christophe
  • 68,716
  • 7
  • 72
  • 138
braaterAfrikaaner
  • 1,072
  • 10
  • 20
  • 2
    1) "_I assumed that thanks to polymorphism I can always put a pointer to a derived class where a pointer to Base class is expected_" Why would you assume it? 2) "_However, now I I get the error "Error: class "Base" has no member "iD"._" Does `Base` have a member named `iD`? – Algirdas Preidžius Jan 08 '18 at 19:10
  • I'm guessing that you don't know: You don't have to specify the object as the first parameter (like self in python) because it is implicit, so you can use the class fields directly inside the method. – pzelasko Jan 08 '18 at 19:11
  • 2
    `override` would also spot first error. – Jarod42 Jan 08 '18 at 19:13
  • pzelasko, you are "guessing" wrong. foo operates on another instance of the class, not on *this. As you can see bar does not take an instance of DerivedWrapper. – braaterAfrikaaner Jan 08 '18 at 19:26

2 Answers2

1

The problem

In your base class you define the pure virtual member function:

virtual void foo(Base&, const int&, const int&) const = 0;

but you provide in the derived a function with another signature:

void foo(Derived&, const int&, const int&) const;

So you have one more member function in the derived class (an overload: same name but different parameter types), but still the inherit the pure virtual function. So the derived class is as abstract as the base class and you're not allowed to instantiate it.

The solution

In the derived class change the signature so that it matches the pure virtual bas e member function:

void foo(Base&, const int&, const int&) const;

By the way, whenever you use virtual functions, use override keyword to spot these kind of subtle errors at compilation, even for ordinary virtual functions:

void foo(Base&, const int&, const int&) const override;

More infos

Indeed, once you define your foo() with a Base parameter, you can't use easily the iD member.

The first solution is to use a dynamic_cast to tell your code that the base is in fact a derived object. Of course you have to check if this assumption is correct:

void Derived::foo(Base& d, const int& i1, const int& i2) const{ 
    Derived *dp = dynamic_cast<Derived*>(&d);
    if (dp) {
        std::cout << dp->iD; 
    }
}

However, what is not clear to me is why you need this first in a first place. Why not get rid of this first type dependent parameter and use the member variables of the current object:

void Derived::foo(const int& i1, const int& i2) const{ 
    std::cout << iD; 
}
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • but then I lose access to iD, since Base does not have that member. – braaterAfrikaaner Jan 08 '18 at 19:29
  • @braaterAfrikaaner then you have no choice but to go for the base class (for the override to work) with dynamic_cast (for using the interface of the derived, as explained in the edited answer – Christophe Jan 09 '18 at 18:06
  • doesn' that (casting) create a copy of the object? The reason I pass a reference to Base into foo is because I am manipulating an object which sits outside the scope of the function. If I make a copy, I'd be manipulating the copy and not the object I care about. – braaterAfrikaaner Jan 13 '18 at 17:51
  • @braaterAfrikaaner no, the dynamic_cast doesn't create a copy of the object. It just returns a pointer to the derived object (or nullptr if the type of the pointed object can't match the type of the pointer). So you can manipulate via this pointer the original object. Here a short demo: https://ideone.com/sthznM – Christophe Jan 13 '18 at 18:29
0

Eventually, I came accross this : What are the differences between a pointer variable and a reference variable in C++?

I conclude that since :

  1. A reference cannot be re-bound, and must be bound at initialization, then the line :

    rBase = rDeriv;

only triggers stBase copy operator, it does'nt rebind rBase.

  1. A reference can be seen as an alias to a typed variable => no polymorphism is possible through a reference.
sylwa06
  • 77
  • 10