В чем смысл Clang's -Wweak-vtables?
Я в принципе не понимаю, Clang's -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 ответ
Если все в классе virtual
методы встроены, у компилятора нет возможности выбрать единицу перевода, в которую нужно поместить одну общую копию виртуальной таблицы - вместо этого копия виртуальной таблицы должна быть помещена в каждый объектный файл, который в ней нуждается. На многих платформах компоновщик может объединять эти несколько копий, либо отбрасывая дубликаты определений, либо сопоставляя все ссылки на одну копию, так что это только предупреждение.
Реализация virtual
Функция out-of-line позволяет компилятору выбирать модуль перевода, который реализует этот метод out-of-line, в качестве "дома" для деталей реализации класса и помещает одну общую копию vtable в тот же модуль перевода. Если несколько методов вне строки, компилятор может сделать произвольный выбор метода, если этот выбор определяется только объявлением класса; например, GCC выбирает первый не встроенный метод в порядке объявления.
Если вы не переопределите какой-либо метод класса, virtual
Ключевое слово не имеет видимого эффекта, поэтому компилятору не нужно выдавать vtable для класса. Если вы не происходят от A
или если вы не можете объявить деструктор производного класса virtual
нет переопределенных методов в A
и поэтому A
vtable опущен. Если вы объявите дополнительный вне линии virtual
метод для подавления предупреждения, а также сделать то, что переопределяет метод в A
, реализация не встроенная virtual
(и сопровождающую его копию виртуальной таблицы) необходимо предоставить в связанном модуле перевода, в противном случае связывание не будет выполнено из-за отсутствия виртуальной таблицы.