简体   繁体   English

尽管违反了One Definition Rule,编译器/链接器COULD如何选择备用内联构造函数?

[英]Despite violation of the One Definition Rule, how is it possible that a compiler/linker COULD choose an alternate inline constructor?

Referring to What determines which class definition is included for identically-named classes in two source files? 参考什么决定在两个源文件中为同名的类包含哪个类定义? , in which there is a deliberate, clear violation of the One Definition Rule , I am still confused how it is even POSSIBLE for the compiler/linker to have the option of selecting one definition over another. 在其中存在故意明确违反单一定义规则的情况 ,我仍然感到困惑的是,编译器/链接器如何选择一个定义而不是另一个定义是可能的。

(ADDENDUM based on answers/comments: I am looking for a single example of how a compiler/linker could produce the result indicated below, given code that is deliberately in violation of the standard and that therefore the code results in undefined behavior.) (附录基于答案/评论:我要寻找的编译器/连接如何产生如下所示的结果只举一个例子,给出的代码是故意标准的侵犯,因此代码产生不确定的行为)

The code sample is: 代码示例是:

// 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?
}

In this case, the function foo() sometimes prints 1, and sometimes 2. 在这种情况下,函数foo()有时会打印1,有时会打印2。

But the constructor for A is inline! 但是A的构造函数是内联的! It's not a function call! 这不是函数调用! Therefore, I would think that the compiler must include the assembly/machine instructions for the code that instantiates the object a within the compiled code for function foo() itself at the time that the function foo() is compiled. 因此,我认为编译器必须包括用于实例化对象的代码的汇编/机器指令a为函数的编译的代码内foo()在该函数的时间本身foo()被编译。

Therefore, I would think that later , at linking time, the linker will NOT change the assembly/machine instructions for the definition of foo() when it decides to include the function foo() in the compiled binary (since it's only known that foo() is, in fact, being called, at linking time). 因此,我认为稍后 ,在链接时,链接器在决定在编译的二进制文件中包含函数foo()时,不会更改foo()定义的汇编/机器指令(因为它只知道foo()事实上, foo()在连接时被称为)。 According to this reasoning, the linker could not possibly influence which inline constructor code is compiled into the function foo() , so it must be file2's version of the inline constructor that is always used, despite the deliberate violation of the One Definition Rule. 根据这种推理,链接器不可能影响将哪个内联构造函数代码编译到函数foo() ,因此它必须是始终使用的内联构造函数的file2版本,尽管故意违反单一定义规则。

If the constructor for A were NOT inline, then I would understand that when the function foo() is compiled, a JUMP statement to a function (the constructor for A ) might be placed inside the assembled code for the function foo() ; 如果A的构造函数不是内联的,那么我会理解,当编译函数foo()时,函数的JUMP语句( A的构造函数)可能放在函数foo()的汇编代码中; then, later, at linkage time, the linker could then fill in the address of the JUMP statement with its choice of the two definitions of the constructor for A . 然后,在链接时,链接器可以填充JUMP语句的地址,并选择A的构造函数的两个定义。

The only explanation I can think of for the fact that in reality, sometime foo() prints 1 and sometimes foo() prints 2 despite the presence of the inline constructor is that the compiler, when it compiles "file2.cpp", creates SPACE in the compiled assembly/machine code representing the function foo() for the inline call to the constructor for A, but does not actually fill in the assembly/machine code itself; 我能想到的唯一解释是,实际上,有时foo()打印1 ,有时foo()打印2尽管内联构造函数的存在是编译器在编译“file2.cpp”时创建SPACE在编译的程序集/机器代码中,表示内联调用A的构造函数的函数foo() ,但实际上并没有填充程序集/机器代码本身; and that later, at linkage time, the linker copies the code for the constructor for A into the pre-determined location within the compiled definition of the function foo() itself, using its (arbitrary) choice between the two definitions of the inline function for the constructor for A . 然后,在链接时,链接器将A的构造函数的代码复制到函数foo()本身的编译定义中的预定位置,使用它在内联函数的两个定义之间的(任意)选择对于A的构造函数。

Is my explanation correct, or is there another explanation? 我的解释是否正确,还是有其他解释? How is it possible, in this example, despite the deliberate violation of the One Definition Rule, for the compiler/linker to have a choice in which constructor for A is called, given that the constructor call is inline? 在这个例子中,尽管故意违反One Definition Rule,编译器/链接器可以选择调用A的构造函数,但是构造函数调用是内联的,这怎么可能呢?

ADDENDUM: I changed the title and added a paragraph of clarification near the top, in response to comments and answers, to make it clear that I understand that the behavior is undefined in this example, and that I am looking for a single example of how a real compiler/linker could produce the observed behavior even once. 附录:我更改了标题并在顶部附近添加了一段澄清,以回应评论和答案,以明确我明白在此示例中行为未定义,并且我正在寻找一个如何的例子真正的编译器/链接器甚至可以产生一次观察到的行为。 Note that I'm not looking for an answer that predicts what the behavior will be at any particular time. 请注意,我不是在寻找能够预测任何特定时间行为的答案。

ADDENDUM 2 : In response to a comment, I have placed a breakpoint at the line A a; 附录2 :在回复评论时,我在A a;线设置了一个断点A a; in the VS debugger and selected the "disassembly" view. 在VS调试器中选择“反汇编”视图。 Indeed, it is plain as day from the disassembly code that DESPITE the presence of "inline", in this case the compiler has chosen NOT to inline the constructor call for the object a : 实际上,从反汇编代码中可以看出,DESPITE存在“内联”,在这种情况下,编译器已选择不内联构造函数调用对象a

线的拆卸视图`A a;从问题中的代码示例:编译器已选择不内联构造函数调用,尽管它是内联的。

Therefore, Alf's answer is correct: Despite the implicit inline of the constructor, the constructor call has NOT been inlined. 因此,Alf的答案是正确的:尽管构造函数隐式inline ,但构造函数调用尚未内联。

A tangential question therefore arises: Can a clear-cut statement be made - one way or the other - regarding whether constructors are less likely to be inlined than regular member functions (assuming inline is present, either explicitly or implicitly, in both cases)? 因此出现了一个切线问题:关于构造函数是否比常规成员函数更不可能被内联(假设inline在两种情况下都明确地或隐含地存在),是否可以做出明确的陈述 - 一种方式或另一种方式? If a statement can be made about this and the answer is "yes, the compiler is more likely to reject inline for a constructor, than it is to reject inline for a regular member function", then a follow-up question would be "why"? 如果可以对此进行声明并且答案为“是,则编译器更可能拒绝inline构造函数,而不是拒绝inline常规成员函数”,那么后续问题将是“为什么“?

Defining your constructor in the class definition is equivalent to using the keyword inline and an out-of-class defintion of it. 在类定义中定义构造函数等效于使用关键字inline和类外定义。

inline does not require/guarantee inline expansion of machine code. inline不要求/保证机器代码的内联扩展。 It hints about that, but that's all. 它暗示了这一点,但就是这样。

The guaranteed effect of inline is to allow the same definition of the function in multiple translation units (it must then be defined, essentially identically, in all translation units where it's used). inline的保证效果是允许在多个翻译单元中使用相同的函数定义(然后必须在使用它的所有翻译单元中对其进行定义,基本相同)。

Thus, your logic based on assumption of required/guaranteed inline expansion of calls, yields incorrect conclusions due to incorrect assumption. 因此,基于假设所需/保证内联扩展调用的逻辑会因错误假设而产生错误的结论。

I'm really surprised that the code may ever link. 我真的很惊讶代码可能会链接。 I'm almost sure it doesn't link with MS Visual Studio, with the linker complaining about names colliding. 我几乎肯定它没有与MS Visual Studio链接,链接器抱怨名称冲突。

In such cases, you could use anonymous namespaces for the definition of your structs if you really don't want to give them different names. 在这种情况下,如果您真的不想为它们指定不同的名称,则可以使用匿名命名空间来定义结构。

Violations of the ODR do not require diagnostics. 违反ODR不需要诊断。 That is, if you do it, the behavior of your program is undefined: anything can happen. 也就是说,如果你这样做,程序的行为是不确定的:任何事情都可能发生。 In the example, the linker probably isn't involved, because all of the member functions are inline, so chances are the code will compile and link without complaint, and do what it looks like it should do. 在该示例中,链接器可能不涉及,因为所有成员函数都是内联的,因此代码可能会编译和链接而没有抱怨,并且做它看起来应该做的事情。 Nevertheless, the standard doesn't promise that that will happen; 尽管如此,该标准并未承诺会发生这种情况; that's what undefined behavior means. 那就是未定义的行为意味着什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM