samedi 29 octobre 2016

Unexpectedly able to call virtual function from base class ctor

Can anyone help explain this unexpected behavior?

The Premise

I've created class Thread that contains a member std::thread variable. Thread's ctor constructs the member std::thread providing a pointer to a static function that calls a pure virtual function (to be implemented by base classes).

The Code

#include <iostream>
#include <thread>
#include <chrono>

namespace
{

class Thread
{
public:
    Thread()
        : mThread(ThreadStart, this)
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl; // This line commented later in the question.
    }

    virtual ~Thread() { }

    static void ThreadStart(void* pObj)
    {
        ((Thread*)pObj)->Run();
    }

    void join()
    {
        mThread.join();
    }

    virtual void Run() = 0;

protected:
    std::thread mThread;
};

class Verbose
{
public:
    Verbose(int i) { std::cout << __PRETTY_FUNCTION__ << ": " << i << std::endl; }
    ~Verbose() { }
};

class A : public Thread
{
public:
    A(int i)
        : Thread()
        , mV(i)
    { }

    virtual ~A() { }

    virtual void Run()
    {
        for (unsigned i = 0; i < 5; ++i)
        {
            std::cout << __PRETTY_FUNCTION__ << ": " << i << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }

protected:
    Verbose mV;
};

}

int main(int argc, char* argv[])
{
    A a(42);
    a.join();

    return 0;
}

The Problem

As you may have already noticed, there's a subtle bug here: Thread::ThreadStart(...) is called from the Thread ctor context, therefore calling a pure/virtual function will not call the derived class' implementation. This is borne out by the runtime error:

pure virtual method called
terminate called without an active exception
Aborted

However, there is unexpected runtime behavior if I remove the call to std::cout in the Thread ctor:

virtual void {anonymous}::A::Run(){anonymous}::Verbose::Verbose(int): : 042

virtual void {anonymous}::A::Run(): 1
virtual void {anonymous}::A::Run(): 2
virtual void {anonymous}::A::Run(): 3
virtual void {anonymous}::A::Run(): 4

I.e. removing the call to std::cout in the Thread ctor seems to have the effect of being able to call a derived class' pure/virtual function from the base class` constructor context! This doesn't align with prior learning and experience.

Build environment in Cygwin x64 on Windows 10. gcc version is:

g++ (GCC) 5.4.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

I'm baffled by this observation and am burning with curiosity about what's going on. Can anyone shed light?

Aucun commentaire:

Enregistrer un commentaire