我今天遇到了一个好奇的虫子,到目前为止我已经设法避开了。
file1.cpp:
#include <iostream>
inline void print() { std::cout << "Print1\n"; }
void y() { print(); }
file2.cpp:
#include <iostream>
inline void print() { std::cout << "Print2\n"; }
void x() { print(); }
main.cpp中:
int x();
int y();
int main(){
x();
y();
}
输出:
Print1 (Expected Print2)
Print1
由于print()
具有内联链接,因此不会产生多重定义错误(使用g++ -Wall file1.cpp file2.cpp main.cpp
编译),并且重复符号会以静默方式折叠。我看到这个的实际情况是使用内联类方法,而不是显式内联函数,但效果是相同的。
我想知道是否有链接器选项或类似的东西,这将允许我在发生此类错误时产生警告?
答案 0 :(得分:2)
没有必要/强制标准生成错误/警告。它违反了C ++的One Definition Rule,因为它们具有不同的功能体。
“extern inline”的含义是 if 对函数的调用不是 生成内联,然后编译器应该使只复制一个 函数的定义,将在所有目标文件之间共享。
如果发生以上情况,则该程序的行为被视为未定义 根据语言标准,但既不编译也不编译 链接器需要提供诊断消息。在实践中,这 意味着,取决于实现的工作方式,编译器或 链接器可能只是默默地选择要使用的定义之一 无处不在。
此行为与此处GCC对 vague linkage 的定义一致。
答案 1 :(得分:2)
这些函数不是内部链接或匿名命名空间,因此它们在外部具有相同的名称。他们有不同的身体,所以你明显违反了一个定义规则。在那时,任何关于将会发生什么的猜测都没有用,因为你的程序格式不正确。
我猜你是在没有优化的情况下编译的,编译器生成了函数调用而不是实际内联,它选择了一个用作调用函数的实体(让另一个孤立)。现在假设您在编译时进行了优化,您的预期输出将被释放,但您的程序仍然是错误的。
编辑评论: 不幸的是,编译器不需要诊断违反一个定义规则(他们甚至可能无法检测到所有情况)。但是你可以做一些事情:
static
或在匿名命名空间中(我更喜欢命名空间用于逻辑分组,但两者都可以。)inline
方法(无论位置如何),因为它们明确告诉编译器所有版本都是相同的(对于非内联方法,您最有可能至少从中获取重复的符号错误连接器)。这里最安全的方法是避免使用全局命名空间内联函数(或者在命名时特别小心)。此外,您需要非常小心,更改#define
不会更改函数体(例如在内联函数中使用assert
,其中一个使用NDEBUG
而另一个不使用{{1}} )。答案 2 :(得分:0)
我将在一个类似主题的here的电子邮件帖子中进行无耻复制粘贴......
这不是gcc问题。
您没有提到您正在使用的操作系统。我会假设它是 GNU / Linux操作系统。在GNU / Linux或任何其他基于ELF的系统上,有一个 全局变量和函数的单个全局命名空间。当两个 共享库使用相同的全局变量名称 到同一个变量。这是一个功能。
如果您想要发生不同的事情,请查看符号可见性 和链接器版本脚本。
看起来您应该能够告诉链接器/编译器符号X应该是唯一的并且如果不是则会抱怨。
答案 3 :(得分:-2)
要使.cpp(或.c)文件的本地函数成为必需,您必须将该函数设为static
。当您在两个单独的.cpp文件中声明打印时,两个文件都编译为print_v
,第二个符号被编译器删除(忽略)*。
这只是假设它在我的机器上的作用。我在MAC OS X山狮上尝试了相同的代码,我得到了与你相同的结果,但我能够通过向两个函数添加static
来解决它。
*如果您更改编译中文件的顺序,行为将会改变。尝试g++ file{2,1}.cpp main.cpp
,输出应为
print2
print2
此致 Abdulrahman Alotaibi