gpt4 book ai didi

c++ - 尽管违反了一个定义规则,但是编译器/链接器如何选择替代的内联构造函数?

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:07:36 25 4
gpt4 key购买 nike

关于What determines which class definition is included for identically-named classes in two source files?,其中有意,明显地违反了One Definition Rule,我仍然感到困惑,即使编译器/链接器可以选择一个定义而不是另一个定义也是如此。

(基于答案/评论的附录:我正在寻找一个示例,说明给定的代码故意违反标准,因此该代码导致未定义的行为,因此编译器/链接器如何产生以下所示的结果。)

代码示例是:

// file1.cpp:

#include <iostream>
#include "file2.h"

struct A
{
A() : a(1) {}
int a;
};

int main()
{
// foo() <-- uncomment this line to draw in file2.cpp's use of class A

A a; // <-- Which version of class A is chosen by the linker?
std::cout << a.a << std::endl; // <-- Is "1" or "2" output?
}

...
//file2.h:

void foo();

...
// file2.cpp:

#include <iostream>
#include "file2.h"

struct A
{
A() : a(2) {}
int a;
};

void foo()
{
A a; // <-- Which version of class A is chosen by the linker?
std::cout << a.a << std::endl; // <-- Is "1" or "2" output?
}

在这种情况下,函数 foo()有时会打印1,有时会打印2。

但是 A的构造函数是内联的!这不是函数调用!因此,我认为编译器必须在编译函数 a时将用于实例化对象 foo()的代码的汇编/机器指令包括在函数 foo()本身的已编译代码中。

因此,我认为以后在链接时,链接器决定将函数 foo()包含在已编译的二进制文件中时,它不会更改 foo()的定义的汇编/机器指令(因为仅知道 foo()实际上是,在链接时被调用)。根据这种推理,链接器可能不会影响将哪个内联构造函数代码编译到函数 foo()中,因此,尽管故意违反了“一个定义规则”,但它必须始终是file2内联构造函数的版本。

如果 A的构造函数不是内联的,那么我会理解,当编译函数 foo()时,可以将对函数的JUMP语句( A的构造函数)放在函数 foo()的汇编代码中;然后,在链接时,链接器随后可以选择 A的构造函数的两个定义来填充JUMP语句的地址。

我可以想到的唯一解释是,实际上,尽管存在内联构造函数,但有时 foo()会打印 1,有时 foo()会打印 2是,编译器在编译“file2.cpp”时会在已编译的程序集中创建SPACE/机器代码,表示对A的构造函数的内联调用的 foo()函数,但实际上未填写汇编/机器代码本身;稍后,在链接时,链接器使用它的内联函数的两个定义之间的(任意)选择,将 A的构造函数的代码复制到函数 foo()本身的已编译定义内的预定位置。 A的构造函数。

我的解释正确吗,或者还有其他解释吗?在此示例中,尽管故意违反了一个定义规则,但如果构造函数调用是内联的,则编译器/链接器如何选择调用 A的构造函数?

ADDENDUM:我更改了标题,并在顶部附近添加了一个澄清段,以回应评论和答案,以明确说明我理解此示例中的行为是未定义的,并且我正在寻找一个示例真正的编译器/链接器如何甚至可以产生一次观察到的行为。请注意,我并不是在寻找可以预测特定时间行为的答案。

ADDENDUM 2 :为了回应评论,我在VS调试器中的 A a;行放置了一个断点,并选择了“反汇编” View 。确实,从反汇编代码开始,尽管不存在“内联”,但在这种情况下,编译器选择不内联对象 a的构造函数调用:

因此,Alf的答案是正确的:尽管构造函数隐含了 inline,但尚未内联构造函数调用。

因此,产生一个切线问题:是否可以以一种方式或另一种方式使用清晰的语句来说明构造函数是否比常规成员函数不太可能内联(假设在两种情况下都显式或隐式地存在 inline)?如果可以对此做出陈述,并且答案是“是的,则编译器更倾向于拒绝构造函数的 inline,而不是拒绝常规成员函数的 inline”,那么后续问题将是“为什么“?

最佳答案

在类定义中定义构造函数等同于使用关键字inline及其父类(super class)定义。
inline不需要/保证机器代码的内联扩展。它暗示了这一点,仅此而已。
inline的保证效果是允许在多个翻译单元中对函数进行相同的定义(然后必须在使用该翻译单元的所有翻译单元中对其进行基本上相同的定义)。

因此,基于错误要求/保证的内联扩展的假设的逻辑将由于错误的假设而得出错误的结论。

关于c++ - 尽管违反了一个定义规则,但是编译器/链接器如何选择替代的内联构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13625629/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com