具有不同定义的内联函数的不可预测行为

时间:2010-01-08 03:49:55

标签: c++ gcc

我有以下源文件:

//test1.cpp
#include <iostream>
using namespace std;

inline void foo()
{
  cout << "test1's foo" << endl;
}

void bar();

int main(int argc, char *argv[])
{
  foo();
  bar();
}

//test2.cpp
#include <iostream>

using namespace std;

inline void foo()
{
    cout << "test2's foo" << endl;
}

void bar()
{
    foo();
}

输出:

test1's foo
test1's foo

咦???好的,所以我应该声明foos是静态的...但是这种事情不应该产生链接器错误,或者至少是警告?编译器如何从编译单元“看到”内联函数?

编辑:这是使用gcc 4.4.1。

3 个答案:

答案 0 :(得分:8)

您正在遇到one-definition-rule。您没有看到任何错误,因为:

  

[某些]违规行为,尤其是那些跨越翻译单位的行为,不需要被诊断

幕后的内容是编译器没有内联这些函数(除非使用优化器编译代码,否则许多编译器不会内联函数)。由于函数是内联的并且可以出现在多个转换单元中,因此编译器会将该函数标记为link-once,它告诉链接器它不会将多个定义视为错误,而只是使用其中一个。

如果你真的希望它们与众不同,你需要一个静态函数。

答案 1 :(得分:5)

R Samuel Klatchko的答案是正确的,但我会回答他没有回答的部分。

“编译器如何从编译单元中”看到“内联函数?”

没有。它看到声明为不是静态的函数的外部定义。对于这样的函数,编译器可以内联,如果它希望,但它也必须生成从外部可调用的代码。

答案 2 :(得分:1)

内联函数放在COMDAT部分中。这是链接器的一个信号,它可以自由选择它遇到的多个定义中的任何一个。当您反转链接顺序时,您将收到另一条输出消息。

在COMDAT部分(允许编译器)中放置定义的另一种方法是:

__declspec(selectany) int globalVariableInHeader = 42;

避免“外部”歌舞是很方便的。显然,这种机制被设计为允许由一个头文件引入的多个定义由多个源文件获得#included由链接器解析。 Fwiw,MSVC具有完全相同的行为。