但是应该如何处理内联函数或模板?
似乎对于内联函数,答案应该是不需要注释.如果定义内联函数的标题的使用者实际内联函数,则不需要符号.如果消费者改为发出外线定义,那仍然可以.唯一的缺点是DLL内部和每个消费库内部的内联函数的定义可能不同.所以,如果你期望可靠地比较内联函数的地址,你可能会遇到麻烦,但无论如何这似乎都很粗略.
鉴于该论点,似乎由于模板主体通常对于消费TU完全可见,因此应用相同的逻辑.
我觉得这里可能存在一些关于’extern模板’和显式实例化的细微之处.
解决方法
正如您在问题中所说,由于内联代码在每个使用该库的模块中都会重新编译,因此对于库的未来版本,可能会出现问题.
我对共享库中的内联函数的建议是它们应该仅用于非常简单的任务或绝对通用的函数.请注意,将公共内联函数转换为非内联函数是ABI中断更改.
例如:
>类似memcpy的功能.排队!
>类似bswap的功能.排队!
>类构造函数.不要内联!即使它现在什么都不做,您可能希望在未来版本的库中执行某些操作.在库中编写非内联空构造函数并将其导出.
>一个类析构函数.不要内联!与上面相同.
在实践中,内联函数可以具有多个不同的地址这一事实并不重要.
关于extern模板和显式实例化,只需要小心,它们就可以用来从库中导出模板.如果模板实例仅限于特定的一组案例,您甚至可以避免将模板代码复制到头文件中.
注1:在下面的例子中,我将使用一个简单的函数模板,但类模板将完全相同.
注2:我正在使用GCC语法. MSC代码是类似的,我想你已经知道差异了(我没有MSC编译器来测试).
例1
public_foo.h
template<int N> int foo(int x); //no-instantiable template
shared_foo.cpp
#include "public_foo.h" //Instantiate and export template __attribute__ ((visibility("default"))) int foo<1>(int x); template __attribute__ ((visibility("default"))) int foo<2>(int x);
program.cpp
#include "public_foo.h" int main() { foo<1>(42); //ok! foo<2>(42); //ok! foo<3>(42); //Linker error! this is not exported and not instantiable }
相反,您的模板应该是可以自由实例化的,但是您希望它以特定的方式经常使用,您可以从库中导出这些模板.想想std :: basic_string:它最有可能被用作std :: basic_string< char>和std :: basic_string< wchar_t>,但不太可能是std :: basic_string< float>.
例2
public_foo.h
template<int N> int foo(int x) { return N*x; } //Do not instantiate these ones: they are exported from the library extern template int foo<1>(int x); extern template int foo<2>(int x);
shared_foo.cpp
#include "public_foo.h" //Instantiate and export template __attribute__ ((visibility("default"))) int foo<1>(int x); template __attribute__ ((visibility("default"))) int foo<2>(int x);
program.cpp
#include "public_foo.h" int main() { foo<1>(42); //ok,from library foo<2>(42); //ok,from library foo<3>(42); //ok,just instantiated }