Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: inexplicable "pure virtual function call" error

I am having a bit of a struggle with Microsoft Visual C++ 2015 and was able to replicate the issue with a small program. Given the following classes:

class BaseClass {
public:
    BaseClass()
        : mValue( 0 )
        , mDirty( true )
    {}
    virtual ~BaseClass() {}
    virtual int getValue() const { if( mDirty ) updateValue(); return mValue; }

protected:
    virtual void updateValue() const = 0;

    mutable bool mDirty;
    mutable int  mValue;
};

class DerivedClass : public BaseClass {
public:
    DerivedClass() {}

protected:
    void updateValue() const override
    {
        mValue++;
        mDirty = false;
    }
};

class Impersonator {
public:
    Impersonator() {}

    // conversion operator
    operator DerivedClass() const
    {
        return DerivedClass();
    }

    // conversion method
    DerivedClass toDerived() const
    {
        return DerivedClass();
    }
};

I get a "pure virtual function call" error when I do the following:

void use( const BaseClass &inst )
{
    // calls `getValue` which in turns calls the virtual function 'updateValue'
    int value = inst.getValue();
}

int main()
{
    // creates a temporary, then passes it by reference:
    use( DerivedClass() ); // this works

    // calls conversion operator to create object on stack, then passes it by reference:
    DerivedClass i = Impersonator();
    use( i ); // this works

    // calls conversion method to create a temporary, then passes it by reference:
    use( Impersonator().toDerived() ); // this works

    // calls conversion operator to create a temporary, then passes it by reference:
    Impersonator j = Impersonator();
    use( j ); // causes a pure virtual function call error!

    return 0;
}

Given that I can't change the void use(const BaseClass&) function, can I change anything in the Impersonator class to allow using the last call without generating a debug error?

like image 233
Paul Houx Avatar asked Aug 01 '16 16:08

Paul Houx


2 Answers

The only way to mitigate the problem that I see is to add an operator const BaseClass&() to Impersonator and have it return a reference to DerivedClass.

This will create a better conversion than the problematic/erroneous one the compiler is trying to use.

Naturally Impersonator won't be able to return by value and create a temporary, so it will have to own a DerivedClass object, or many objects, and dispose them somehow at an appropriate time. The simplest way that works for this demo program is to have it return a reference to its data member, but a real program may have to do something else.

class Impersonator {
public:
    Impersonator() {}

    // conversion operator
    operator DerivedClass()
    {
        return d;
    }
    operator const BaseClass&()
    {
        return d;
    }

private:
    DerivedClass d;
};
like image 88
n. 1.8e9-where's-my-share m. Avatar answered Oct 18 '22 13:10

n. 1.8e9-where's-my-share m.


This is a workaround. Create a wrapper for use which accepts a const DerivedClass&.

//I get a "pure virtual function call" error when I do the following :
void use(const BaseClass &inst)
{
    // calls `getValue` which in turns calls the virtual function 'updateValue'
    int value = inst.getValue();
}

void use(const DerivedClass &inst) {
    use(static_cast<const BaseClass&>(inst));
}

The better match means the workaround wrapper will be selected, so a temporary of the correct type will be created, and a reference to that passed to the real use implementation.

like image 37
Ben Avatar answered Oct 18 '22 13:10

Ben