使用内联链接在冲突定义上生成错误

时间:2013-02-22 19:59:19

标签: c++ linker inline

我今天遇到了一个好奇的虫子,到目前为止我已经设法避开了。

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编译),并且重复符号会以静默方式折叠。我看到这个的实际情况是使用内联类方法,而不是显式内联函数,但效果是相同的。

我想知道是否有链接器选项或类似的东西,这将允许我在发生此类错误时产生警告?

4 个答案:

答案 0 :(得分:2)

没有必要/强制标准生成错误/警告。它违反了C ++的One Definition Rule,因为它们具有不同的功能体。

引自: Extern Inlines by Default

  

“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