RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 991697
Accepted
ghostinecatnewyear
ghostinecatnewyear
Asked:2020-06-11 19:25:48 +0000 UTC2020-06-11 19:25:48 +0000 UTC 2020-06-11 19:25:48 +0000 UTC

为什么链接时头文件中定义的内联函数不重复?

  • 772

我阅读了有关“常规”和内联函数的事实:

在前面的章节中,我们反复说过,你不应该在头文件中定义函数,因为如果你在几个 .cpp 文件中包含一个带有函数定义的头文件,那么函数定义也会被复制多次。然后,当您链接文件时,链接器将抛出一个错误,表明您多次定义相同的函数。

但是,内置函数不受此规则的约束,因为源代码中没有重复 - 函数的定义是一个,并且在通过链接器连接 .cpp 文件时不应有任何冲突。

为什么不能在头中定义函数很清楚:链接包含该头的多个模块时会发生“多重定义”。但是从文本中根本不清楚为什么该规则不适用于内联函数。我是这样理解的:“内联函数不重复,因为它们不重复。” 狂欢。

当然,我可以理所当然地考虑这个事实,仅此而已,但是我仍然想了解为什么会发生这种情况,但是本文没有给出详尽的解释。

c++
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. AnT stands with Russia
    2020-06-11T21:43:17Z2020-06-11T21:43:17Z

    如果语言规范说不应该有错误,那么就不应该有错误。然后开始实施细节。你为什么决定它们不重复?

    在经典实现中,编译器决定在编译多个翻译单元时为其生成主体的具有外部绑定的内联函数当然是重复的。在编译期间,每个翻译单元都会在其目标文件中获得自己的同名函数副本。

    然而,这些函数在目标文件中以一种特殊的方式被标记——这样当在链接过程中发现同一个外部符号的多个副本时,链接器不会给出错误,而是默默地删除所有副本,只留下一个。也就是说,C++ 编译器生成散布在不同目标文件中的同名函数的“转储”,然后链接器将所有内容收集在一起并清理此“转储”。

    在 *nix 系列的编译器中,这个标记是导出符号的名称,即所谓的。“弱”(弱)性格。MSVC++ 编译器中有类似的符号selectany。只有在链接过程中遇到两个或更多相同的“强”字符时,链接器才会抛出多重定义错误。如果只有一个“强”符号(其余为“弱”),则“强”符号获胜,“弱”符号被丢弃。如果根本没有“强”符号,而只有“弱”符号,则“弱”符号中的一个(一些)获胜。没有错误报告。

    当编译器决定为具有外部链接的内联函数生成主体时,它只是将链接器的相应符号标记为“弱”,仅此而已。

    (模板函数的翻译建立在相同的机制上,如您所知,它也在头文件中定义,并且还在所有需要实例化的目标文件中生成它们的副本。)

    例如,通过将这样一个简单的源代码编译到目标文件中

    inline void bar() {}
    
    void (*foo())()
    {
      return bar;
    }
    

    并查看此目标文件的内容,nm我们将看到

    0000000000000000 W _Z3barv
    0000000000000000 T _Z3foov
    

    一个字母W标记一个“弱”字符,一个字母标记T一个“强”字符。在生成此类函数主体的所有目标文件中inline,它将以相同的名称出现,并_Z3barv带有标记W。


    请注意,通过生成多个具有不同名称的函数来解决这个问题是不可能的:在所有其他方面,内联外界函数的行为应该与任何其他外界函数一样,例如,它必须具有相同的所有翻译单元中的地址。


    这种方法的副作用是形成目标文件的“经典”方法,其中函数没有开始和结束,只有一个入口点,变得不可接受。为了能够从目标文件中排除函数,C++ 编译器被迫在目标文件中以紧凑的形式形成函数体。


    历史上有一些尝试采取不同行动的替代实现的例子。“多遍”实现在第一次编译时根本没有为内联和模板函数生成主体。他们进行了预链接,在那里他们收集了有关哪些函数实际需要主体的信息,然后再次调用编译器并为这些函数编译独特的主体,然后进行最终链接。但是在流行的编译器(GCC / Clang / MSVC)中,这种方法并没有扎根。

    • 14
  2. Best Answer
    zcorvid
    2020-06-11T19:30:31Z2020-06-11T19:30:31Z

    如果一个内联函数真的被内联了,那么它就好像它不存在一样,所以原则上不能重复。

    如果优化后它没有被内联,那么它在每个翻译单元中都有一个唯一的名字,结果,我们有很多函数做同样的事情,但有不同的名字。由于名称最终不同,因此不会发生多重定义。

    在此处阅读更多内容:C++11 标准、3.2.5(一个定义规则)、7.1.2(函数说明符)。

    我在哪里可以获得 C++ 标准?

    • 3

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5