Почему явное создание экземпляра шаблона приводит к предупреждению о слабых шаблонах-таблицах, когда существуют виртуальные системы вне линии? - PullRequest
7 голосов
/ 08 мая 2019

[Отредактировано, чтобы показать разделение между .cpp и hpp]

// file.hpp
class Base {
 public:
    virtual ~Base(void);
    Base(void);
    Base(const Base&) = default;
};

template<typename T>
class Derived: public Base {
 public:
    Derived(void);
    bool func(void);
};
// file.cpp
#include "file.hpp"

Base::~Base(void) {}
Base::Base(void) {}

template<typename T>
bool Derived<T>::func(void) {return true;}

template<typename T>
Derived<T>::Derived(void) {}

// required to avoid linker errors when using `Derived` elsewhere
template class Derived<int>;

Последняя строка вызывает следующее предупреждение компилятора в Clang v8.0 warning: explicit template instantiation 'Derived<int>' will emit a vtable in every translation unit [-Wweak-template-vtables]

Насколько я понимаю, поскольку Base имеет хотя бы один виртуальный метод вне строки, виртуальные таблицы для всех классов здесь будут привязаны к этой единице перевода, поэтому это руководство в кодировании LLVM стандарт. Так почему генерируется это предупреждение?

Смотрите на Godbolt здесь с конкретной версией компилятора, которую я использую: https://godbolt.org/z/Kus4bq

Каждый подобный вопрос, который я нахожу в SO, относится к классам без внеплановых виртуальных методов, поэтому я не смог найти ответ.

Ответы [ 2 ]

2 голосов
/ 08 мая 2019

Существует ли строка template class Derived<int>; в заголовочном файле, который снова включен в несколько исходных файлов?

В этом случае, vtable и методы класса Derived<int> будут существовать в нескольких объектах-файлы.И компоновщик должен выяснить, что делать с этими несколькими копиями.

Как компилятор и компоновщик должны разрешать это в соответствии со стандартом c ++, я не уверен в этом.Но, как правило, мне все равно, поскольку копии обычно должны выглядеть одинаково.

Но чтобы избежать этой проблемы, вы должны поместить extern template class Derived<int>; в заголовочный файл и template class Derived<int>; ровно в 1 единицу компиляции (иначе. source-file)

EDIT (чтобы отразить ваше разделение кода на «file.hpp» и «file.cpp»):

Я немного поиграл с clang-6(Это последняя версия, которая у меня есть)

Для меня предупреждение относится к типу «Если вы сделаете X, произойдет Y».Но это не значит, что у произошло.

В этом случае Y - это несколько vtables, и это произойдет, только если вы поместите template class Derived<int>; в несколько исходных файлов, чего вы не делаете.

Предупреждение срабатывает для каждого template class Derived<int>; в ваших источниках, поэтому, если вы видите только одно предупреждение, будет только один vtable.

Но есть способ избавиться от предупреждения: неиметь явное создание экземпляра и полагаться на то, что компилятор неявно создает экземпляр класса.

Для этого вы должны поместить все определения вашего шаблона в заголовочный файл.Поэтому переместите определения:

template<typename T>
bool Derived<T>::func(void) {return true;}

template<typename T>
Derived<T>::Derived(void) {}

в заголовочный файл и удалите extern template class Derived<int>; и template class Derived<int>;

0 голосов
/ 08 мая 2019

Я склонен полагать, что это связано с ошибкой в ​​Clang, о которой сообщается здесь: https://bugs.llvm.org/show_bug.cgi?id=18733

Повторное размещение контента в случае разрыва ссылки:

Рафаэль Авила де Эспиндола 2014-02-05 00:00:19 PST

Дано

template<typename T>
class foo {
  virtual ~foo() {}
};

extern template class foo<int>;
template class foo<int>;

Clang предупреждает:

test.cpp:6:23: warning: explicit template instantiation 'foo<int>' will emit a vtable in every translation unit [-Wweak-template-vtables]
extern template class foo<int>;
                      ^
1 warning generated.

обратите внимание, что предупреждение указывает на явное объявление создания шаблона, но срабатывает по определению.Вероятно, следует проверить, находится ли определение в заголовке.В файле .cpp есть только одна единица перевода, которая его видит.

Комментарий 1 David Faure 2016-02-13 12:21:27 PST

Да,этот ложный позитив действительно раздражает.Спасибо, что сообщили об этом.Разработчики Clang: спасибо за исправление: -)

Я был бы признателен за мнение кого-либо еще, но я согласен с докладчиком об ошибках, что это предупреждение кажется поддельным в этом случае.

Хотя последний комментарий к сообщению об ошибке относится к нему как к исправленному, ошибка по-прежнему указана в статусе «новый», поэтому я не верю, что она исправлена.

...