2011-07-27 23 views
8

我已经花了几天时间在一个奇怪的问题,并最终发现有两个inline功能在项目中相同的签名,他们造成了这个问题。为了简化这里的情况就是一个例子:两个CPP文件:如果重新定义内联函数会怎样?

a.cpp

#include <iostream> 

void b(); 

inline void echo() 
{ 
    std::cout << 0 << std::endl; 
} 

int main() 
{ 
    echo(); 
    b(); 
    return 0; 
} 

和b.cpp

#include <iostream> 

inline void echo() 
{ 
    std::cout << 1 << std::endl; 
} 

void b() 
{ 
    echo(); 
} 

请注意:inline功能echo具有相同签名但不同实现。编译并运行

g++ a.cpp b.cpp -o a.out && ./a.out 

或者这样

g++ a.cpp -c 
g++ b.cpp -c 
g++ a.o b.o -o a.out 
./a.out 

它打印0 0。 (我用的是G ++ 4.6.1是什么,以及我铿锵++ 2.9,相同的结果测试)

如果开启优化,像

g++ -O3 a.cpp b.cpp -o a.out && ./a.out 

这是0 1这个时候,就不会发生。

我的问题是,无论结果如何或编译如何执行,都没有错误甚至是警告我已多次定义了inline函数。编译器和链接器在这种情况下究竟发生了什么?

编辑:

采取目标文件

nm a.o b.o | c++filt 

两个文件都记录在echo()看看符号。所以我认为问题发生在链接时。难道说链接器会随机选择一个实现并丢弃所有其他实现吗?

+0

你有没有试过更高级的警告(-Wall等)? – schnaader

+0

我刚刚尝试过'-Wall -Wextra',仍然没有任何警告。 – neuront

回答

5

编译器不需要诊断此ODR违例,它不是微不足道的。 inline关键字表示不同的翻译单元可能具有相同的符号,因此编译器将其标记为弱。基本用例是一个在头文件中内联定义的函数:包含头文件的所有翻译单元都将具有该定义,并且它非常好。编译器只需要放弃除一个定义之外的所有定义,并在任何地方使用该定义。

检测不同的定义是否完全匹配是一个复杂的问题。链接器必须分析生成的二进制实现,并确定这两个二进制代码是否与相同的源代码相关。大多数编译器没有支持来确定这一点。

当你的具体问题,我不可能知道,导致了两个功能被嵌入标记的理由,但一个常见的错误是使用inline关键字来表示优化而非在不抱怨的重复链接时间inline关键字在头文件中有意义,但在cpp文件中没有这么多。在cpp文件中,如果要将某段代码分解到辅助函数中,该函数应标记为static或在未命名的命名空间中定义。如果函数是static,那么编译器知道该函数的所有用法都在你的翻译单元中,并且它有更多的知识来决定是否内联或不内函数(注意,即使你不用不要告诉它,就像它可以决定不内联即使你告诉它一样)。

12

在C++标准中指出比内联函数的所有定义应是相同的,但没有诊断是必需的。也就是说,你的程序不是一个有效的C++程序,但是实现有权不检测那个错误。

参见条款3.2.5。张贴这里太长了。

6

这种情况下(两个内联函数具有相同的名称和相同的签名具有不同的实现)leads to undefined behavior。编译器不需要诊断它,尽管它可以尝试。

相关问题