Why do I get linker errors trying to build C++ code using pure virtual functions within a template?

StackOverflow https://stackoverflow.com/questions/4210987

  •  25-09-2019
  •  | 
  •  

Question

In an application I'm currently writing, I created a template class with a pure virtual function, then an another class inheriting an instance of the former and implementing the virtual function. The virtual function is called from the parent's constructor, which is used by the child also. I can't build this code due to linker errors and I can't figure out why. Here's a simplified version of the code to reproduce the issue I'm having.

template <typename T> class Foo
{
public:
    Foo(T a)
    {
        x = a;
        echo();
    }

protected:
    T x;
    virtual void echo() = 0;
};

class Bar : public Foo<int>
{
public:
    Bar(int a) : Foo<int>(a)
    {
    }

    void echo();
};

void Bar::echo()
{
    cout << "value: " << x << endl;
}

int main(int argc, char* argv[])
{
    Bar bar(100);
    return 0;
}

The linker error appears as follows in MSVC:

purevirttest.obj : error LNK2019: unresolved external symbol "protected: virtual void __thiscall Foo::echo(void)" (?echo@?$Foo@H@@MAEXXZ) referenced in function "public: __thiscall Foo::Foo(int)" (??0?$Foo@H@@QAE@H@Z)

If I move the call to echo() from Foo's constructor, the code builds and executes nicely, I can call bar.echo() without any problems. Problem is I'd really like that function in the constructor. Any explanation of this mystery higly appreciated.

Was it helpful?

Solution

James McNellis' answer that "You can't call echo() from the constructor of Foo<T>" is almost correct.

You can't call it virtually from a Foo<T> constructor, because while the body of the Foo<T> constructor executes the object is of type Foo<T>. There's no derived class part yet. And a virtual call of echo(), as in your code, then goes to pure virtual function: bang, dead.

However, you can provide an implementation of a pure virtual function like echo(), and then call it non-virtually, like Foo::echo(), from the Foo constructor. :-) Except that that calls the Foo implementation. While it seems you'd like to call the derived class' implementation.

Now regarding your problem:

"I'd really like that function in the constructor."

Well, as I'm writing this your (invalid) code looks like this:

template <typename T> class Foo
{
public:
    Foo(T a)
    {
        x = a;
        echo();
    }

protected:
    T x;
    virtual void echo() = 0;
};

class Bar : public Foo<int>
{
public:
    Bar(int a) : Foo<int>(a)
    {
    }

    void echo();
};

void Bar::echo()
{
    cout << "value: " << x << endl;
}

int main(int argc, char* argv[])
{
    Bar bar(100);
    return 0;
}

And as far as I understand your problem description, you want the Foo constructor to call the echo implementation of whatever class it is that inherits from Foo.

There are a number of ways to do that; they're all about bringing knowledge of the derived class' implementation to the base class.

One is known as CRTP, the Curiously Recurring Template Pattern, and adapted to your particular problem it can go like this:

#include <iostream>

template< class XType, class Derived >
class Foo
{
public:
    Foo( XType const& a )
        : state_( a )
    {
        Derived::echo( state_ );
    }

protected:
    struct State
    {
        XType   x_;
        State( XType const& x ): x_( x ) {}
    };

private:
    State   state_;
};

class Bar
    : public Foo< int, Bar >
{
private:
    typedef Foo< int, Bar >     Base;
public:
    Bar( int a ): Base( a ) {}
    static void echo( Base::State const& );
};

void Bar::echo( Base::State const& fooState )
{
    using namespace std;
    cout << "value: " << fooState.x_ << endl;
}

int main()
{
    Bar bar(100);
}

The above is a solution that isn't bad, but isn't good either. If your actual problem is to call a derived class non-static member function from the base class constructor then the only "good" answer is Java or C#, which let you do such thing. It's intentionally not supported in C++, because it's very easy to then inadvertently try to access as-yet uninitialized stuff in the derived class object.

Anyways, as nearly always, where there is a compile time solution of something, there's also a run time solution.

You can simply pass the function to be executed as a constructor argument, like so:

#include <iostream>

template< class XType >
class Foo
{
protected:
    struct State
    {
        XType   x_;
        State( XType const& x ): x_( x ) {}
    };

public:
    Foo( XType const& a, void (*echo)( State const& ) )
        : state_( a )
    {
        echo( state_ );
    }

private:
    State   state_;
};

class Bar
    : public Foo< int >
{
private:
    typedef Foo< int >  Base;
public:
    Bar( int a ): Base( a, echo ) {}
    static void echo( Base::State const& );
};

void Bar::echo( Base::State const& fooState )
{
    using namespace std;
    cout << "value: " << fooState.x_ << endl;
}

int main()
{
    Bar bar(100);
}

If you study those two programs you'll probably note a subtle difference (in addition to compile time versus run time knowledge transfer).

Finally, there are solutions involving dirty casts and there is also a loophole in the C++ type system that lets you access that protected base class state without casting, by using member pointers. The former is dangerous and latter is obscure and possibly inefficient. So, don't.

But hopefully one of the solutions above will suit you, or some suitable adaption.

Oh, by the way, the more general set of problems that yours seems to be an instance of, is known as DBDI, Dynamic Binding During Initialization. You can find a more general treatment of it in the C++ FAQ item 23.6 Okay, but is there a way to simulate that behavior as if dynamic binding worked on the this object within my base class's constructor?. Also, for the special case where the DBDI is that you want a part of the base class construction to be controlled/supplied by the derived class, see my blog entry "How to avoid post-construction by using Parts Factories".

Cheers & hth.,

OTHER TIPS

You can't call echo() from the constructor of Foo<T>.

Inside of the constructor of Foo<T>, the dynamic type of the object is Foo<T>. It's not until after the Foo<T> constructor finishes that the dynamic type becomes Bar.

Since echo() is pure virtual in Foo<T> and since Foo<T> is the dynamic type of the object, you cannot call echo() in the constructor of Foo<T>.

Unless you are very familiar with how the dynamic type of an object changes during construction and destruction, it would be a good idea not to try calling virtual functions from constructors and destructors.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top