编写库时是否应将可见性/导出宏应用于模板? 当构建一个C++ DLL或共享库< Cyp>Ay AtditTeX((x,ViiBiItIythyOy(“默认”)))或Oy-DeScript(DLLuturn)经常通过宏连接到应该被库用户所使用的那些具体符号(类、函数等),其他符号默认具有内部可见性

编写库时是否应将可见性/导出宏应用于模板? 当构建一个C++ DLL或共享库< Cyp>Ay AtditTeX((x,ViiBiItIythyOy(“默认”)))或Oy-DeScript(DLLuturn)经常通过宏连接到应该被库用户所使用的那些具体符号(类、函数等),其他符号默认具有内部可见性,c++,dll,shared-libraries,visibility,C++,Dll,Shared Libraries,Visibility,但是对于内联函数或模板应该怎么做呢 似乎对于内联函数,答案应该是不需要注释。如果定义内联函数的头的使用者实际内联函数,则不需要符号。如果消费者发出了一个不符合要求的定义,那么仍然可以。唯一的问题是DLL和每个使用库中的内联函数的定义可能不同。因此,如果您希望可靠地比较内联函数的地址,可能会遇到一些麻烦,但这似乎很粗略 鉴于这一论点,似乎由于模板体通常对消费TU是完全可见的,所以同样的逻辑也适用 我觉得关于“外部模板”和显式实例化,这里可能有一些微妙之处 有人对可见性属性应该如何与内联函数和模板保

但是对于内联函数或模板应该怎么做呢

似乎对于内联函数,答案应该是不需要注释。如果定义内联函数的头的使用者实际内联函数,则不需要符号。如果消费者发出了一个不符合要求的定义,那么仍然可以。唯一的问题是DLL和每个使用库中的内联函数的定义可能不同。因此,如果您希望可靠地比较内联函数的地址,可能会遇到一些麻烦,但这似乎很粗略

鉴于这一论点,似乎由于模板体通常对消费TU是完全可见的,所以同样的逻辑也适用

我觉得关于“外部模板”和显式实例化,这里可能有一些微妙之处


有人对可见性属性应该如何与内联函数和模板保持一致有具体的指导吗?

内联函数在外部不可见(没有链接,IIRC),因此它们不能从DLL导出。如果它们是公共的,那么它们将完全写入库的头文件中,并且每个用户都会重新编译它

正如您在问题中所说,由于内联代码在使用库的每个模块中都会重新编译,因此对于库的未来版本可能会出现问题

对于共享库中的内联函数,我的建议是,它们应该只用于非常简单的任务或绝对通用的函数。请注意,将公共内联函数转换为非内联函数是一个破坏ABI的更改

例如:

  • 类似于
    memcpy
    的函数。内联
  • 类似于bswap的
    函数。内联
    
  • 类构造函数。不要内联!即使它现在什么也不做,您也可能希望在该库的未来版本中做一些事情。在库中编写非内联空构造函数并将其导出
  • 类析构函数。不要内联!同上
实际上,一个内联函数可以有几个不同的地址这一事实并不重要

关于外部模板显式实例化,需要注意的是,它们可以用于从库导出模板。如果模板实例化仅限于一组特定的情况,您甚至可以避免将模板代码复制到头文件中

注1:在下面的示例中,我将使用一个简单的函数模板,但类模板的工作原理完全相同。 注2:我使用的是GCC语法。MSC代码是类似的,我想你已经知道它们的区别了(而且我没有MSC编译器来测试)

例1 公屋 共享_foo.cpp
#包括“public_foo.h”
//实例化并导出
模板属性(可见性(“默认”))
intfoo(intx);
模板属性(可见性(“默认”))
intfoo(intx);
program.cpp
#包括“public_foo.h”
int main()
{
foo(42);//好的,从图书馆
foo(42);//好的,从图书馆
foo(42);//好的,刚刚实例化
}

我想你的问题可以归结为以下两个问题:

何时需要显式导出具有
\uuuuuu属性((\uuuu可见性\uuuuuuu(“默认”))
的符号

这里的规则是,如果该方法的实现是共享库的内部实现(通常在.cpp文件中,在external.h中有一个声明),则在编译共享库时,需要将该符号的声明标记为外部可见。如果您没有做到这一点,那么在外部库中编译任何使用该方法的东西都会在链接时发出抱怨——因此这个问题很容易通过测试发现

这是假设您还添加了
-fvisibility=hidden
,以默认内部化所有符号

向具有外部可见定义的内联函数或模板函数添加
\uuuuuuuu属性((\uuuuu可见性\uuuuuuuuuu(“默认”))
是否有害

事实并非如此,因为默认可见性的一个常见用例是将整个类标记为导出,这可能包括outline和inline方法。在这种情况下,内联方法将是内联的(在使用它的任何翻译单元中),并且不会生成外部符号。如果以后选择概述该方法,符号可见性将生效


对于模板,显式模板实例化只是一种允许您为模板方法创建大纲定义的机制,并且与普通内联/大纲方法的规则相同。

感谢您的详细回答。你在这里所说的与我的理解一致,但我需要第二种意见。谢谢。还有。。。这里有一条新的皱纹:
template<int N> int foo(int x); //no-instantiable template
#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);
#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
}
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);
#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);
#include "public_foo.h"

int main()
{
    foo<1>(42); //ok, from library
    foo<2>(42); //ok, from library
    foo<3>(42); //ok, just instantiated
}