如何在头文件库中定义(非方法)函数

4
当编写头文件库(例如Boost)时,是否可以定义非方法的自由浮动函数,而不会(1)使生成的二进制文件膨胀和(2)遭受“未使用”警告?
当我在一个头文件中定义一个函数,该头文件被多个源文件包含,这些源文件链接到同一个二进制文件时,链接器会抱怨重定义。解决此问题的一种方法是使函数静态化,但这会在每个翻译单元中复制代码(顺便问一下,链接器能够安全地去重吗?)。此外,这会触发编译器对函数未使用的警告。
我试图寻找Boost中自由浮动函数的示例,但我找不到。技巧是将所有内容都包含在类(或模板)中吗?

为什么你想要做这样的事情? - AnT stands with Russia
顺便问一下,链接器能安全地去重这些吗?使用内联可能会更好。 - peterchen
有特定于实现的功能可以在链接阶段解决这个问题。比如微软实现中的__declspec(selectany)以及GCC中的__attribute__((weak)) - AnT stands with Russia
5个回答

12
如果你真的想要定义函数(而不是声明它),你需要使用 inline 来防止链接错误。
否则,你可以在头文件中声明该函数,并在源文件中单独提供其实现。

当然,使用“inline”会增加函数被内联的机会,这可能会导致使用该头文件的应用程序中出现代码膨胀。 - Vladimir Prus

8
您可以使用 inline 关键字:
inline void wont_give_linker_errors(void)
{
    // ...
}

2
嗯...你问题的答案很简单,就是不要在头文件中定义函数,除非是内联函数。
"静态"函数也可以在头文件中定义,但它只对非常特定的罕见目的有用。仅仅为了解决多重定义问题而使用"静态"是无稽之谈。
同样地,头文件是用于非定义函数声明的。地球上为什么要在那里定义函数呢?
你说你正在编写"头文件库"。什么是"头文件库"?请注意,Boost在头文件中定义其"函数",因为它们的"函数"实际上不是函数,它们是函数模板。函数模板必须在头文件中定义(嗯,几乎是)。如果不是这样,Boost就不会做一些像在头文件中定义任何东西这样奇怪的事情了。

额,模板。:) 尽管如此,这是一个很好的观点。我在回答中解决了使用“static”的技术性质,但没有涉及其正确性。 - Troubadour
如果原始贴主在定义模板的话,他就不会有第一道题目中的多个定义问题了。此外,在这里有一个术语上的区别我一直在尝试遵守:函数模板并不是一个函数 - AnT stands with Russia
是的,我知道如果他在使用模板,就不会有那个问题。那和面包价格有什么关系?我想我只能相信你所说的“函数”是指非模板函数,显然你的编辑是在我的评论之后出现的。很抱歉我给你点了赞。 - Troubadour
嗯,也许我误解了你的评论(我仍然不明白你被冒犯了什么)。但即使我的编辑出现在你的评论之后,我提交编辑后才看到了你的评论(也就是说,在我忙于进行编辑时,你提交了评论)。当然,这只能由我来证明。 - AnT stands with Russia

1
除了已经提到的inline之外,大多数编译器都需要在头文件中定义模板(并且所有编译器都允许这样做)。由于boost主要是模板,这就解释了为什么它几乎全部都是头文件。

-1

人们建议使用inline,但这违反了您问题的第一部分,即每次调用函数时都会将完整定义插入到代码中,从而使代码膨胀。因此,您的总体问题的答案是“不行”。

如果您将它们标记为static,那么它们仍然在每个源文件中定义,正如您所指出的那样,但只有一次,因此如果代码大小是唯一的问题,那么这是一个比inline更好的选择。我不知道链接器是否可以或者是否被允许检测到重复项并将它们合并。我怀疑不行。

编辑

只是为了澄清是否支持在头文件中使用static和/或定义函数的观点,我保证我不支持。这只是作为对标记为inline和在头文件中定义的static函数之间差异的技术响应。没有其他意思。


1
inline 有两个作用:第一,防止多次定义成为错误。第二,它是向编译器发出的提示,表明此函数可以内联。在实践中,编译器不会内联大型函数(除非例如它是唯一的出现)。 - peterchen
难道不是相反的吗?那些静态函数难道不会触发“未使用”的警告吗?(静态不是意味着每个包含文件都有自己的函数版本,既然它是“定制的”,如果它们都在某个地方使用它,那确实很好?) - UncleBens
@peterchen:我知道这只是一个提示,所以我在描述最坏的情况,这是我没有表明清楚的事情。 - Troubadour
1
@Troubadour:我的意思是,你谈到了需求1),但建议的东西几乎肯定无法满足需求2)。此外,静态是否意味着相同的函数将被生成多次,从而使exe变大?另外,您认为编译器不能选择内联(或不内联)静态函数,只要它看到实现即可吗?(就我所知,“inline”的目的是让您在头文件中定义函数,否则编译器别无选择,但不进行内联。) - UncleBens
@Troubadour:你所忽略的是编译器需要内联函数调用的唯一条件就是拥有该函数的完整定义(链接器可以内联其他函数调用)。只要满足给定优化设置的启发式规则并且函数体在头文件中定义,编译器也可以内联静态函数。 - UncleBens
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接