Когда компилятор может статически связать вызов виртуальной функции? - PullRequest
13 голосов
/ 03 сентября 2011

Я ожидал, что компилятор сможет статически разрешать вызов функции для виртуальной функции, если тип класса известен во время компиляции (например, если экземпляр класса не используется через ссылку или указатель, как показано наСлучай 1) ниже).

Однако я наблюдал странное поведение с компилятором C ++ Visual Studio 2010, и я хотел бы знать, есть ли у компилятора какая-либо причина не статически связывать вызовы «правильной» виртуальной функции, когда экземплярыклассов с виртуальными функциями являются членами в структуре, доступ к которой осуществляется по ссылке.

Стоит ли ожидать от компилятора статической привязки вызовов функции f () в случае 2) ниже?Является ли «эталонная» сущность cr как-то распространяющейся на cr.a, хотя a является A, а не 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  
}

Ответы [ 2 ]

3 голосов
/ 03 сентября 2011

Очевидно, что авторы компилятора не удосужились решить этот случай. Возможно, в реальном коде это недостаточно распространено, чтобы стоить их внимания.

Если GetACRef определено в другом месте, также возможно, что C там полиморфен, что может повлиять на оптимизацию.

Обратите внимание, что компилятор не решает все возможные случаи, когда небольшая тестовая программа "очевидна" для человека. Основное внимание компиляторов уделяется случаям, которые часто случаются в больших реальных программах.

2 голосов
/ 03 сентября 2011

Я не знаю, почему MSVC не компилирует ваши сценарии "Case 2" для прямых вызовов - это, безусловно, возможно.Я думаю, что только Microsoft сможет ответить.

Обратите внимание, что GCC выполняет оптимизацию, которую вы ищете (протестировано с MinGW 4.5.1 и -O2).

ТакжеMSVC использовала диспетчеризацию vtable даже для следующей последовательности (для ясности - я использую опцию оптимизации /Ox):

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

Так что нет необходимости в функции или структуре контейнера для добавления слоев потенциалапутаница с компилятором - нет причины, по которой компилятор не может обрабатывать ar.f() точно так же, как a.f() в этой последовательности.Но, как предположил Бо Перссон, возможно, это не очень распространенный сценарий оптимизации (или MS просто не добралась до него).Снова, только разработчики компилятора в MS могли ответить.

Я не уверен, что классифицировал бы это поведение как «странное», хотя - это возможность оптимизации, которая упущена, и все.Я не уверен, насколько распространенными могут быть такие вещи.Стоит ли ожидать, что компилятор будет генерировать статически связанные вызовы в этом случае?Может быть.Но я думаю, что не слишком удивительно, что этого не происходит.

Может быть, проблема должна быть открыта в MS Connect.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...