Инверсия порядка сгенерированных vtable функций для одноименных функций - PullRequest
0 голосов
/ 01 мая 2020

Если, используя Visual Studio 2019, я скомпилирую этот код C ++ с двумя виртуальными методами, имеющими одно и то же имя, но с разными аргументами:

struct MyStruct
{
    virtual void foo(float) = 0;
    virtual void foo(int) = 0;
};

class MyClass : public MyStruct
{
public:
    void foo(float) {}
    void foo(int) {}
};

static MyClass c;

Порядок методов в генерируемом классе vtable инвертируется. Вот вывод в https://godbolt.org

const MyClass::`vftable' DQ FLAT:const MyClass::`RTTI Complete Object Locator'          ; MyClass::`vftable'
    DQ      FLAT:virtual void MyClass::foo(int)
    DQ      FLAT:virtual void MyClass::foo(float)

Если я различаю имена (например, foo1 и foo2), порядок в сгенерированном коде такой же, как в моем объявлении.

Это нормальное поведение для компилятора C ++? Если да, как определяется порядок?

1 Ответ

1 голос
/ 02 мая 2020

Короткий ответ: компоновка vtable определенно зависит от компилятора. Фактически, языковой стандарт не требует, чтобы компилятор даже использовал vtables для реализации диспетчеризации виртуальных функций.

При этом в конкретном случае Windows и Visual C ++:

  • C ++ vtables преднамеренно размечены так, чтобы быть совместимыми с соглашениями о вызовах COM, которые требуют, чтобы слоты были последовательно назначены для виртуальных функций;

  • также для взаимодействия COM простое наследование добавляет новые виртуальные функции в конец родительской таблицы;

  • однако COM не допускает перегрузок, т. е. одноименных функций с разными сигнатурами.

Случай OP нарушает последний пункт, поэтому гарантии COM здесь не применяются, поскольку интерфейс не является COM-совместимым с самого начала из-за перегрузок. Фактически, Microsoft явно предупреждает для C# до избегать перегрузок в видимых интерфейсах COM .

Таким образом, технически поведение компилятора VC ++ не нарушает никаких правил, ни языка, ни COM. Кроме того, я не знаю ни одного параметра / трюка / регресса для принудительного определения определенного порядка перегрузок в виртуальной таблице.

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

struct MyHiddenStruct
{
    virtual void foo(float) = 0; 
};

struct MyStruct : MyHiddenStruct
{
    MyHiddenStruct::foo;
    virtual void foo(int) = 0; 
};

class MyClass : public MyStruct
{
public:
    void foo(float) { }
    void foo(int) { }
};

[ EDIT ] Нашел похожие VS 2010 q & a в Visual C ++ методы в vfptr в обратный порядок со строгим указанием на то, что перегрузки в одном классе сгруппированы в vtable в обратном порядке объявления. Поэтому, что бы ни делал VS 2019 в наши дни, это не новый каприз.

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