最早是在下面的程序中发现的问题:
// a.cpp
struct foo
{
void bar(){printf("foo::bar in a.cpp\n");}
};
void testa()
{
foo f;
f.bar();
}
// b.cpp
struct foo
{
void bar(){printf("foo::bar in b.cpp\n");}
};
voidtestb()
{
foo f;
f.bar();
}
// main.cpp
int main(int argc, char** argv)
{
testa();
testb();
return 0;
};
结果我发现这个程序居然输出:
foo::bar in a.cpp
foo::bar in a.cpp
先怀疑是编译器的 bug, 不过很快否定了(因为编译阶段并不管具体链接的符号). 继而怀疑是链接器的 bug, 但最后发现还是我的 bug, 因为上面的代码实际上等价于:
// test.h
struct foo
{
inline void bar();
};
// a.cpp
#include "test.h"
inline void foo::bar(){printf("foo::bar in a.cpp\n");}
void testa()
{
foo f;
f.bar();
}
// b.cpp
#include "test.h"
inline void foo::bar(){printf("foo::bar in b.cpp\n");}
void testb()
{
foo f;
f.bar();
}
// main.cpp
int main(int argc, char** argv)
{
testa();
testb();
return 0;
};
由于 foo::bar
是内联的, 所以允许每个编译单元有一个自己的实现, 但在链接阶段, 链接器是不可能发现这两个实现不同的(理论上可能,但工作量太大了), 所以它会假设实现相同(毕竟大家一般都把内联函数写在头文件中), 并把所有的引用指向同一个实现(如果分别指向本编译单元内部自有的实现肯定是错的, 考虑函数中定义了静态变量的情形), 这也就出现了本文中的问题. 如果 foo::bar
不是内联的, 则会有一个链接错误。
这里 bar
是作为一个成员函数出现的, 但它是非成员函数也有同样的问题, 所以结论就是: 不要在同一个名字空间中定义原型相同但实现不同的同名内联函数。
ps: 狗年最后一篇,祝大家新春愉快,万事如意,合家欢乐。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于