У меня есть две динамические библиотеки и один исполняемый файл:
В качестве теста, чтобы увидеть, когда вызывается определенная функция, я добавил встроенная функция из libOtherLibrary.so (детали кода не должны иметь значения):
template<class T>
inline void className<T>::clear() const
{
Info << "Hello World!" << endl; // My message!
if (isTmp() && ptr_)
{
if (ptr_->unique())
{
delete ptr_;
ptr_ = 0;
}
else
{
ptr_->operator--();
ptr_ = 0;
}
}
}
Я потом перекомпилировал libOtherLibrary.so, с последующей перекомпиляцией libMyLibrary.so. Наконец-то я перекомпоновывается (так что без перекомпиляции) EXE.
В результате любой вызов className<T>::clear()
начато в libMyLibrary.so использовал старый реализация этого встроенного метода, тогда как любой вызов className<T>::clear()
по инициативе libOtherLibrary.так использовал новый реализация.
Когда я решил также перекомпилировать EXE (с последующим его связыванием), результатом было то, что новый реализация всегда использовалась.
Мой вопрос: может ли кто-нибудь объяснить мне, почему EXE требуется перекомпиляция, а не перекомпоновка?
То есть встраивание функции className<T>::clear()
из libOtherLibrary.so должно происходить на этапе компиляции libMyLibrary.so, не так ли? В конце концов, это функция, содержащаяся в libMyLibrary.so кому звонит className<T>::clear()
, Тогда я ожидаю, что связь EXE достаточно, так как EXE не вызывает эту конкретную встроенную функцию. Один компоновщик позаботится о любой измененной совместимости ABI.
Мой вопрос: может кто-нибудь объяснить мне, почему exe требуется
перекомпиляция, а не перекомпоновка?
Потому что, для вашего конкретного варианта использования, без него вы навлечете на себя гнев Нарушение ODR.
В результате любой вызов
className<T>::clear()
начато в
libMyLibrary.so использовал старый реализация этого встроенного метода, тогда как любой вызовclassName<T>::clear()
по инициативе
libOtherLibrary.так использовал новый реализация.
Когда у вас есть функция-шаблон сказать:
template<class T>
inline void className<T>::clear(){
....
}
И это ODR используется в нескольких единицах перевода (файл .cpp). Это экземпляр будет определен в каждом из таких единиц перевода, потому что Функции-шаблоны неявно inline
,
Правила для такого множественного определения изложены здесь basic.def.odr / 6. И одно из перечисленных требований гласит, что «каждое определение D должно состоять из одной и той же последовательности токенов»;.
Изменение этого шаблона функции и перекомпиляция некоторых модулей перевода, использующих его в ODR, и связывание вашей программы без перекомпиляции все единицы перевода, использующие ODR, нарушают святое Правило Единого Определения C ++.
Наборы инструментов компилятора не обязаны диагностировать это.
Еще один способ сказать это:
Предполагая, компилятор делает встраивание (который не связан с inline
ключевое слово, например НКУ постараюсь встроить, если вы компилируете и ссылка с g++ -flto -O2
четные функции, которые не отмечен inline
), код должен быть перекомпилирован, как только изменится определение встроенной функции. В большинстве хороших программ на C ++ это определение встречается в некоторых заголовок файлы (содержащие явно inline
функции).
Поэтому вы должны перекомпилировать при изменении заголовочных файлов. Хорошие инструменты автоматизации сборки (например, make
в сочетании с g++ -MD
) справиться с этим.