Search code examples
c++c++11constructorpure-virtualvisual-studio-2013

Why does VS2013 use a non-virtual and a virtual call, respectively, in the first and the second examples below?


First example: pure virtual function foo() is called in the Base constructor with a non-virtual call. That explains why the code executes normally, i.e., it doesn't abort like the second example.

#include <iostream>
struct Base {
    Base() { foo(); }
    virtual void foo() = 0;
};

void Base::foo() { std::cout << "Base::foo()\n"; }

struct Derived : Base { void foo() { std::cout << "Derived::foo()\n"; } };

int main()
{
    Derived d;
}

Second example: here the pure virtual function foo() is also called in the Base ctor, but with a virtual call and the code aborts with R6025 - pure virtual function call.

#include <iostream>

struct Base {
    Base() { call_foo(); }
    virtual void foo() = 0;
    void call_foo() { foo(); }
};

void Base::foo() { std::cout << "Base::foo()\n"; }

struct Derived : Base { void foo() { std::cout << "Derived::foo()\n"; } };

int main()
{
    Derived d;
}

I know by §10.4/6 that pure virtual functions called from constructors or destructors is considered undefined behavior. But I'm curious to know what could be a reasonable explanation for the different calls to foo() in these two snippets?


Solution

  • As others have said, it is undefined behavior. What is (probably) happening, however, is that in the first case (the call in the constructor), the compiler knows the dynamic type of the object (since it is always the type being constructed), and so generates the call exactly as it would for calling any non-virtual function. (If you had failed to provide a definition, I imagine that the linker would have complained.) In the second case, you call the function from within another function. At that point, the compiler has no way of knowing the dynamic type the object will have at runtime; the function could have been called on a fully constructed object, perhaps of a derived type not even present in this source file. Since the compiler cannot determine statically the dynamic type the object will have at runtime, it must generate a call through the virtual function table (or whatever method it uses to resolve dynamic dispatch, but VS, like everyone else I know, uses a vptr to a vtable). Since calling a pure virtual function in this manner is undefined behavior, the compiler puts a pointer to an error handling routine in the vtable.

    Note that in this case, the compiler could have seen that the call in the constructor of Base would resolve in a manner which causes undefined behavior, and generated code which would have triggered the error. Or... since all of the virtual functions of Base are pure virtual, the compiler could have known that the cannot be called during construction, and not even bothered to create a separate vtable for Base, perhaps initializing the vptr to point to the vtable of Derived before calling the constructor of Base. You can't count on anything such cases.