C++:当动态库中的内联函数发生变化时要重新编译什么?

C++: What to recompile when an inline function in a dynamic library changes?

我有两个动态库和一个可执行文件:

作为查看何时调用特定函数的测试,我向 libOtherLibrary.so[=64 的 内联函数 添加了打印语句=](代码细节无关紧要):

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。最后我relinked(所以没有重新编译)exe.

结果是在 libMyLibrary.so 中发起的对 className<T>::clear() 的任何调用都使用了此的 old 实现内联方法,而任何由 libOtherLibrary 发起的对 className<T>::clear() 的调用。因此使用了 new 实现。

当我随后决定也重新编译 exe(然后链接它)时,结果是始终使用 new 实现.


我的问题是:谁能给我解释一下为什么 exe 需要 重新编译,而不是仅重新链接

libOtherLibrary.so的函数className<T>::clear()的内联应该发生在[=92=的编译阶段],不是吗?毕竟是libMyLibrary.so中包含的函数调用className<T>::clear()。然后我希望链接 exe 就足够了,因为 exe 不会调用这个特定的内联函数。链接器将单独处理任何更改的 ABI 兼容性。

My question is: Can someone explain to me why exe required recompilation, rather than relinking only?

因为,对于您的特定用例,如果没有它,您将招致 ODR violation 的愤怒。


The result was that any call to className<T>::clear() initiated in libMyLibrary.so used the old implementation of this inline method, whereas any call to className<T>::clear() initiated by libOtherLibrary.so used the new implementation.

当你有一个函数模板时说:

template<class T>
inline void className<T>::clear(){
    ....
}

并且在多个翻译单元(.cpp 文件)中是ODR used。它的实例化将在每个这样的翻译单元中定义,因为 function-templates 是隐式的 inline.

此处说明了此类多重定义的规则basic.def.odr/6。列出的要求之一是 "each definition of D shall consist of the same sequence of tokens;".

修改该函数模板并重新编译一些使用 ODR 的翻译单元,并链接您的程序,而不重新编译所有 使用它的翻译单元 ODR 违反了圣洁C++的一种定义规则。

诊断它不需要编译器工具链。

另一种说法:

假设编译器 inlining (which is unrelated to inline keyword, e.g. GCC会努力内联如果你编译和link g++ -flto -O2 即使 不是 标记为 inline 的函数,只要内联函数的定义发生变化,就应该重新编译代码。在大多数优秀的 C++ 程序中,该定义出现在某些 header 文件中(明确包含 inline 函数)。

所以当头文件改变时你应该重新编译。好的构建自动化工具(例如 makeg++ -MD 相结合)可以解决这个问题。