c++performancevisual-studio-2010polymorphism

When can the compiler statically bind a call to a virtual function?


I expected the compiler to be able to statically resolve a function call to a virtual function if the type of the class is known at compile time (e.g. if the class instance is not being used through a reference or a pointer as illustrated in Case 1) below).

However, I have observed a strange behavior with Visual Studio 2010's C++ compiler and I would like to know if there is any reason for the compiler not to statically bind the calls to the "right" virtual function when the instances of the classes with the virtual functions are members in a structure that is being accessed by reference.

Should I expect the compiler to statically bind the calls to f() in Case 2) below? Is the "reference"ness of cr somehow propagating to cr.a even though a is an A and not an A&?

struct A
{
    virtual void f() ;
    virtual ~A() ;
};

struct B : A
{
    virtual void f() ;
    virtual ~B() ;
};

struct C {
    A a ;
    B b ;
};

C & GetACRef() ;

void test()
{
    // Case 1) The following calls to f() are statically bound i.e.
    // f() is called without looking up the virtual function ptr.
    C c ;  
    c.a.f() ;
    c.b.f() ;
    A a ;
    a.f() ;

    // Case 2) The following calls to f() go through the dynamic dispatching
    // virtual function lookup code. You can check if you generate the .asm
    // for this file.
    C & cr = GetACRef() ; // Note that C is not polymorphic
    cr.a.f() ; // visual C++ 2010 generates call to f using virtual dispatching
    cr.b.f() ; // visual C++ 2010 generates call to f using virtual dispatching  
}

Solution

  • I don't know why MSVC doesn't compile your "Case 2" scenarios to direct calls - it's certainly possible. I think that only Microsoft would be able to answer.

    Note that GCC does perform the optimization that you're looking for (tested with MinGW 4.5.1 and -O2).

    Also, MSVC used vtable dispatch even for the following sequence (for clarity - I'm using the /Ox optimization option):

    A a;
    A& ar(a);
    ar.f();
    

    So there's no need for a function or container struct to add layers of potential confusion to the compiler - there's no reason the compiler can't treat ar.f() exactly the same as a.f() in that sequence. But as Bo Persson has suggested, perhaps it's not an extremely common optimization scenario (or MS have just plain not gotten around to it). Again, only the compiler devs at MS could answer.

    I'm not sure I'd classify this behavior as 'strange' though - it's an optimization opportunity that's missed is all. I'm not sure how common this kind of thing might be. Should you expect the compiler to generate statically bound calls in this case? Maybe. But I think it's not too surprising that it doesn't happen.

    Maybe an issue should be opened on MS Connect.