如果重新定义内联函数怎么办?

neu*_*ont 8 c++ compiler-construction linker inline redefinition

我花了几天时间处理一个奇怪的问题,最后发现项目中有两个inline相同签名的功能,导致了问题.为了简化这种情况,这里有一个例子:两个cpp文件:

a.cpp

#include <iostream>

void b();

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

int main()
{
    echo();
    b();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

和b.cpp

#include <iostream>

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

void b()
{
    echo();
}
Run Code Online (Sandbox Code Playgroud)

请注意,inline功能echo具有相同的签名但不同的工具.编译并运行

g++ a.cpp b.cpp -o a.out && ./a.out
Run Code Online (Sandbox Code Playgroud)

或者像这样

g++ a.cpp -c
g++ b.cpp -c
g++ a.o b.o -o a.out
./a.out
Run Code Online (Sandbox Code Playgroud)

它打印0 0.(我正在使用g ++ 4.6.1,我用clang ++ 2.9测试,结果相同)

如果启用优化,就不会发生这种情况

g++ -O3 a.cpp b.cpp -o a.out && ./a.out
Run Code Online (Sandbox Code Playgroud)

现在是0 1这个时候.

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

编辑:

看一下目标文件中的符号

nm a.o b.o | c++filt
Run Code Online (Sandbox Code Playgroud)

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

Arm*_*yan 13

在C++标准中陈述的内联函数的所有定义应该相同,但不需要诊断.也就是说,您的程序不是有效的C++程序,但实现有权不检测该错误.

见第3.2.5条.这里发帖太久了.


sha*_*oth 6

这种情况(具有相同名称和具有不同实现的相同签名的两个内联函数)导致未定义的行为.虽然可以尝试,但编译器不需要诊断它.


Dav*_*eas 5

编译器不需要诊断此ODR违规,这并非易事.该inline关键字是指不同的翻译单位可能具有相同的符号,因此它被标记由编译器弱.基本用例是在标题中内联定义的函数:包含标题的所有翻译单元都将具有定义,并且完全没问题.编译器只需要丢弃除一个定义之外的所有定义,并在任何地方使用该定义.

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

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