Що означає clang's -Wweak-vtables?


78

Я в основному не розумію кланг -Wweak-vtables. Ось те, що я спостерігав до цього часу:

Випадок перший: (викликає попередження)

class A {
    public:
    virtual ~A(){}        
};

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Випадок другий: (не викликає попередження)

class A {
    public:
    virtual ~A(){}        
};   

int main(){}

Випадок третій: (не викликає попередження)

class A {
    public:
    virtual ~A();

};

A::~A(){}

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Випадок чотири: (Попередження про спрацьовування)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}        
};    

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Випадок п'ятий: (не викликає попередження)

class A {
    public:
    virtual ~A(){}
    virtual void fun();      
};    

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Випадок шостий: (не викликає попередження)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {};

int main(){}

Випадок сьомий: (не викликає попередження)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {
    public:
    virtual void fun(){}
};

int main(){}

Точне попередження -

warning: 'A' has no out-of-line virtual method definitions; its vtable 
will be emitted in every translation unit [-Wweak-vtables]

Очевидно, якщо я не оголошую некласну віртуальну функцію в класі, це спричиняє якусь проблему тоді і лише тоді, коли я походжу від неї, а похідний клас має віртуальний деструктор.

Запитання:

  1. Чому це проблема?
  2. Чому це виправляється оголошенням віртуальної функції? (Попередження говорить про визначення)
  3. Чому попередження не виникає, коли я не походжу з класу?
  4. Чому попередження не виникає, коли похідний клас не має віртуального деструктора?

Відповіді:


102

Якщо всі virtualметоди класу вбудовані, компілятор не має можливості вибрати одиницю перекладу, в яку помістити одну спільну копію vtable - замість цього, копія vtable повинна бути розміщена у кожному об'єктному файлі, який потребує цього. На багатьох платформах компоновщик здатний уніфікувати ці множинні копії, або відкидаючи повторювані визначення, або зіставляючи всі посилання на одну копію, тому це лише попередження.

Реалізація virtualфункції out-of-line дозволяє компілятору вибрати модуль перекладу, який реалізує цей метод out-of-line як "дім" для деталей реалізації класу, і розміщує єдину спільну копію vtable в тому ж блоці перекладу . Якщо кілька методів не входять в рядок, компілятор може зробити довільний вибір методу, доки цей вибір визначається лише декларацією класу; наприклад, GCC вибирає перший невбудований метод у порядку декларування.

Якщо ви не заміните жоден метод класу, virtualключове слово не має спостережуваного ефекту, тому компілятор не потребує випуску vtable для класу. Якщо ви не отримуєте похід від A, або якщо вам не вдається оголосити деструктор похідного класу virtual, немає перевизначених методів, Aі, отже A, vtable пропущено. Якщо ви задекларуєте додатковий virtualметод, що не відповідає лінії, для придушення попередження, а також зробите щось, що замінює метод у A, реалізація не вбудованого virtual(та супровідна копія vtable) повинна бути надана у зв’язаному модулі перекладу , інакше зв’язок не вдасться, оскільки відсутність vtable.


2
Що це робить, коли (різні) позалінійні методи одного класу трапляються в декількох різних ТУ?
MM

2
@Matt, один позалінійний метод вибирається будь-яким довільним, але детермінованим способом на основі оголошення класу, і vtable опиняється в тому самому TU, що і реалізація методу. Усі відповідні ТУ бачать одну і ту ж декларацію: забороняють несумісні декларації, вони домовляться про обраний метод і, таким чином, спільно видають рівно одну VTAB.
Джеффрі Хантін,

Ця відповідь не пояснює випадки 6 та 7.
Леон,

2
@JeffreyHantin Що ви маєте на увазі під "деструктором B не є віртуальним" ? Згенерований компілятором (неявно оголошений) деструктор Bповинен бути віртуальним (оскільки деструктор базового класу оголошено virtual).
Леон

2
@Leon, додайте специфікацію: "Деструктор, який за замовчуванням і не визначений як видалений, неявно визначається, коли він використовується odr (3.2) або коли він явно за замовчуванням після першого оголошення." Ніщо у випадках 6 або 7 не використовує або явно за замовчуванням ~B(), тому визначення за замовчуванням компілятором неявно не створюється. Як він може бути віртуальним, якщо його не існує? Тривіальність прикладів спричиняє деякі неінтуїтивні ефекти.
Джеффрі Хантін,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.