Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot initialize object parameter of type 'Parent' with an expression of type 'Derived'

I'm implementing a simple hierarchy: System and Device would be two base classes. A System has Devices. Then I'd have a Network derived from System, which will have Computers. Computer is derived from both System and Device. And finally a Computer has various Devices like CPU, Memory etc. The reason Computer is a Device is because Network is a System and Systems store Devices. The reason Computer is a System is because it stores Devices (CPU, Memory etc.)

Anyway, my implementation so far:

class Device {
public:
    explicit Device(unsigned property): property(property) {}
    friend void swap(Device &first, Device &second) {
        using std::swap;
        swap(first.property, second.property);
    }
    Device& operator=(Device other) {
        swap(*this, other);
        return *this;
    }
private:
    Device() = default;

    unsigned property;
};

class System {
public:
    explicit System(const string &name): name(name) {}
    friend void swap(System &first, System &second) {
        using std::swap;
        swap(first.name, second.name);
        swap(first.devices, second.devices);
    }
    System& operator=(System other) {
        swap(*this, other);
        return *this;
    }
protected:
    void addDevice(Device b1) {
        devices.push_back(b1);
    }
private:
    vector<Device> devices;
    string name;
};

class Computer: public System, public Device {
public:
    Computer(unsigned property, const string& name): System(name), Device(property) {}

    Computer& addAddress(const string &address) {
        addresses.emplace_back(address);
        return *this;
    }
    Computer& addComponent(Device newDevice) {
        System::addDevice(newDevice); //error
        return *this;
    }
private:
    vector<const string> addresses;
};

class Network: public System {
public:
    Network(const string& name): System(name) {}

    Network& addComputer(Device newComputer) {
        System::addDevice(newComputer); //no error
        return *this;
    }
};

class CPU: public Device {
public:
    CPU(unsigned coreCount, unsigned frequency): Device(coreCount), frequency(frequency) {}
private:
    unsigned frequency;
};

Computer's addComponent() has an error because cannot initialize object parameter of type System with an expression of type Computer. I'm not entirely sure what that means since that function is called from a Computer class, which is a Device hence it should have access to the parent's addDevice(). The parameter the function has is a Device.

It's also confusing that my Network class, where the function addComputer() uses the same set of instructions has no error.

Could someone explain how my design is flawed?

like image 528
Le Nguyen Duy Anh Avatar asked Apr 25 '19 19:04

Le Nguyen Duy Anh


1 Answers

This error message popped up for me today, while I was in the processes of coding the derived class, and in my case the key was that the derived class was--just at that moment--un-instantialble.1

As soon as I made enough progress on the derived class for it to be instantiated the errors evaporated.

Cause

The derived call from which you are typing to invoke the base class is currently un-instantiable.

Demonstration

Consider the following code which has two inheritance relationships.

class Type {
public:
    Type(){}
    virtual ~Type() {}
    virtual void m() = 0;
};

class SubType: public Type {
public:
    SubType(): Type() {}
    virtual ~SubType() {}
    // virtual void m() override; // <== Important code commented out here
};
// void SubType::m() {}           // <== and here


class Base {
public:
    Base() {};
    virtual ~Base() {}
    virtual void m();
};

class Derived: public Base, public Type {
public:
    Derived(): Base() {}
    virtual ~Derived() override {}
    virtual void m() override;
protected:
    SubType m_troublesome;  // <== Note member here has incomplete type when
                            //     comments are in place
};

void Base::m() {}

void Derived::m() {
    Base::m();              // <== Tricky error message on this line
}

Attempt to compile this with

$ clang --std=c++11 -Wall -pedantic -c test.cc

and you receive the feedback

test.cc:34:13: error: field type 'SubType' is an abstract class
    SubType m_troublesome;
            ^
test.cc:8:18: note: unimplemented pure virtual method 'm' in 'SubType'
    virtual void m() = 0;
                 ^
test.cc:42:11: error: cannot initialize object parameter of type 'Base' with an
      expression of type 'Derived'
    Base::m();

If you see all three together you know where to start, but if you are using clang as a linter (as many IDEs do), then you may see the third one (which occurs on the line Base::m();, a perfectly valid piece of code in isolation) in a context divorced from the first two.

Instant confusion.

If you remove the comments from the two marked sections of code (making SubType complete and thus making it possible to instantiate Derivedobjects), the code compiles cleanly.

Two possible lessons here:

  • Finish what you start. If I hadn't left the analog of SubType half done the situation would never have arisen.
  • If the linter is being confusing look at the full error output of the build.

1 In particular I had a member declared with a type in which I hadn't yet overridden an inherited abstract virtual method.

like image 105
dmckee --- ex-moderator kitten Avatar answered Sep 17 '22 03:09

dmckee --- ex-moderator kitten