Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/dart/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ ODR的意图是什么?_C++_Language Design_Linkage_One Definition Rule - Fatal编程技术网

C++ ODR的意图是什么?

C++ ODR的意图是什么?,c++,language-design,linkage,one-definition-rule,C++,Language Design,Linkage,One Definition Rule,我确实理解ODR所说的,但我不理解它试图实现什么 我看到违反它的两个后果-用户将得到语法错误,这是完全正确的。而且可能会有一些致命的错误,同样,用户是唯一有罪的人 作为违反ODR并得到一些致命错误的示例,我想象如下: Class layout (32-bit, alignment member): 0x00: char {c} 0x01: [alignment member, 3 bytes] 0x04: string {str} 0x14

我确实理解ODR所说的,但我不理解它试图实现什么

我看到违反它的两个后果-用户将得到语法错误,这是完全正确的。而且可能会有一些致命的错误,同样,用户是唯一有罪的人

作为违反ODR并得到一些致命错误的示例,我想象如下:

Class layout (32-bit, alignment member):
0x00: char                {c}
0x01: [alignment member, 3 bytes]
0x04: string              {str}
0x14: short               {s}
0x16: [alignment member, 2 bytes]
0x18: unsigned long long  {ull}
0x20: float               {f}
Size: 36 bytes.

Class layout (64-bit, alignment member):
0x00: char                {c}
0x01: [alignment member, 3 bytes]
0x04: string              {str}
0x1C: short               {s}
0x1E: [alignment member, 2 bytes]
0x20: unsigned long long  {ull}
0x28: float               {f}
Size: 44 bytes.

Class layout (32-bit, reordered):
0x00: string              {str}
0x10: unsigned long long  {ull}
0x18: float               {f}
0x1C: short               {s}
0x1E: char                {c}
Size: 31 bytes.

Class layout (64-bit, reordered):
0x00: string              {str}
0x18: unsigned long long  {ull}
0x20: float               {f}
0x24: short               {s}
0x26: char                {c}
Size: 39 bytes.
a、 cpp 如果示例与ODR无关,请纠正我

那么,ODR是否试图禁止用户做这种有害的事情?我不这么认为

它是否试图为编译器编写者设置一些规则,以避免违反规则带来的潜在危害?可能不会,因为大多数编译器不检查ODR冲突


还有什么?

当函数希望获得这些结构中的一个时,您将其重新声明为不同的结构,该函数接收哪个结构,以及如何接收?请记住,C++是静态的,所以如果按值发送结构,函数必须知道它的结构。因为C++是类型安全的,允许违反ODR会违反这种类型的安全性。
最重要的是,缺少ODR有什么好处?我能想到成百上千的事情,如果没有它,会变得更难,而没有任何收获。从字面上讲,在同一名称空间中踩踏以前声明的类型是没有灵活性的。在最佳情况下,它只会使多个包含不需要头保护,这是一个非常小的增益最多。

< P> ODR决定C++程序是如何形成的。ODR冲突意味着您的程序格式不正确,标准没有规定程序将做什么,如果它应该编译,等等。大多数ODR冲突被标记为“无需诊断”,以使编译器编写器的工作更容易

< >这允许C++编译器对你所给的代码做出某些简化的假设,比如<>代码>:A/<代码>到处都是相同的结构类型,并且不必在每个使用点检查。


编译器可以免费获取您的代码并将其编译为c:。或者别的什么。它可以自由地检测ODR冲突,并使用它来证明代码分支无法运行,并消除指向该分支的路径。

据我所知,该规则的目的是防止对象在不同的翻译单元中被不同地定义

// a.cpp
#include <iostream>

class SharedClass {
    int a, b, c;
    bool d;
    int e, f, g;

  public:
    // ...
};

void a(const SharedClass& sc) {
    std::cout << "sc.a: " << sc.getA() << '\n'
              << "sc.e: " << sc.getE() << '\n'
              << "sc.c: " << sc.getC() << std::endl;
}

// -----

// b.cpp
class SharedClass {
    int b, e, g, a;
    bool d;
    int c, f;

  public:
    // ...
};

void b(SharedClass& sc) {
    sc.setA(sc.getA() - 13);
    sc.setG(sc.getG() * 2);
    sc.setD(true);
}

// -----

// main.cpp
int main() {
    SharedClass sc;
    /* Assume that the compiler doesn't get confused & have a heart attack,
     *  and uses the definition in "a.cpp".
     * Assume that by the definition in "a.cpp", this instance has:
     *   a = 3
     *   b = 5
     *   c = 1
     *   d = false
     *   e = 42
     *   f = -129
     *   g = 8
     */

    // ...

    a(sc); // Outputs sc.a, sc.e, and sc.c.
    b(sc); // Supposedly modifies sc.a, sc.g, and sc.d.
    a(sc); // Does NOT do what you think it does.
}
如果编译器将大小相同的字段按从大到小的顺序放在一起:

// a.cpp
Class layout:
0x00: int  {a}
0x04: int  {b}
0x08: int  {c}
0x0C: int  {e}
0x10: int  {f}
0x14: int  {g}
0x18: bool {d}
Size: 25 bytes.

// b.cpp
Class layout:
0x00: int  {b}
0x04: int  {e}
0x08: int  {g}
0x0C: int  {a}
0x10: int  {c}
0x14: int  {f}
0x18: bool {d}
Size: 25 bytes.

// main.cpp
One of the above, up to the compiler.
Alternatively, may be seen as undefined.
如果愿意,请注意,虽然类在两个定义中的大小相同,但其成员的顺序完全不同

Field comparison (with alignment member):
a.cpp field     b.cpp field
a               b
b               e
c               g
d & {align}     a
e               d & {align}
f               c
g               f

Field comparison (with hidden reordering):
a.cpp field     b.cpp field
a               b
b               e
c               g
e               a
f               c
g               f
d               d
因此,从
a()
的角度来看,
b()
实际上改变了
sc.e
sc.c
,或者
sc.a
或者
sc.d
(取决于编译方式),完全改变了第二次调用的输出。[请注意,这甚至可能出现在您从未预料到的假定无害的情况下,例如
a.cpp
b.cpp
SharedClass
具有相同的定义,但指定了不同的对齐方式。这将更改对齐成员的大小,再次为类提供不同的内存布局不同的翻译单位。]

现在,如果相同的字段在不同的翻译单元中有不同的布局,就会发生这种情况。想象一下,如果类在不同的单元中有完全不同的字段,会发生什么

// c.cpp
#include <string>
#include <utility>

// Assume alignment of 4.
// Assume std::string stores a pointer to string memory, size_t (as long long), and pointer
//  to allocator in its body, and is thus 16 (on 32-bit) or 24 (on 64-bit) bytes.
// (Note that this is likely not the ACTUAL size of std::string, but I'm just using it for an
//  example.)
class SharedClass {
    char c;
    std::string str;
    short s;
    unsigned long long ull;
    float f;

  public:
    // ...
};

void c(SharedClass& sc, std::string str) {
    sc.setStr(std::move(str));
}
这个
SharedClass
不仅有不同的字段,而且大小完全不同。试图把每一个翻译单元当作它们拥有相同的
SharedClass
可以而且将会破坏某些东西,而默默地协调每个定义是不可能的。想象一下,如果我们在
SharedClass
的同一个实例上调用
a()
b()
c()
,会发生什么混乱,甚至如果我们尝试创建
SharedClass
的实例会发生什么。有了三个不同的定义,而编译器不知道哪一个是真正的定义,事情可能会也会变得糟糕

这完全破坏了单元间的可操作性,要求使用类的所有代码要么在同一个翻译单元中,要么在每个单元中共享完全相同的类定义。因此,ODR要求每个单元只定义一次类,并在所有单元中共享相同的定义,以保证它始终具有相同的定义,并防止整个问题


类似地,考虑这个简单函数,<代码>函数():< /> > < /P> 在这种情况下,每个翻译单元定义变量

global\u int
Globals::ns\u int
,这意味着程序将有两个完全相同的名称的不同变量。这只能在链接阶段结束,链接器将符号的每个实例视为引用同一实体
Globals::ns_int
将比
global_int
有更多问题,因为有两个不同的初始化值硬编码到文件中;假设链接器不只是爆炸,程序保证有未定义的行为


ODR的复杂性因所涉及的实体而异。有些东西在整个程序中只能有一个定义,但有些东西可以有多个定义,只要它们完全相同并且每个翻译单元只有一个。无论是哪种情况,目的都是让每个单位都能以完全相同的方式看到实体

不过,主要原因是方便。编译器不仅可以更容易地假设每个翻译单元都遵循ODR,而且速度更快,占用的CPU、内存和磁盘更少。如果没有ODR,编译器必须比较每个翻译单元,以确保每个共享类型和内联函数定义都相同,并且每个
Field comparison (with alignment member):
a.cpp field     b.cpp field
a               b
b               e
c               g
d & {align}     a
e               d & {align}
f               c
g               f

Field comparison (with hidden reordering):
a.cpp field     b.cpp field
a               b
b               e
c               g
e               a
f               c
g               f
d               d
// c.cpp
#include <string>
#include <utility>

// Assume alignment of 4.
// Assume std::string stores a pointer to string memory, size_t (as long long), and pointer
//  to allocator in its body, and is thus 16 (on 32-bit) or 24 (on 64-bit) bytes.
// (Note that this is likely not the ACTUAL size of std::string, but I'm just using it for an
//  example.)
class SharedClass {
    char c;
    std::string str;
    short s;
    unsigned long long ull;
    float f;

  public:
    // ...
};

void c(SharedClass& sc, std::string str) {
    sc.setStr(std::move(str));
}
Class layout (32-bit, alignment member):
0x00: char                {c}
0x01: [alignment member, 3 bytes]
0x04: string              {str}
0x14: short               {s}
0x16: [alignment member, 2 bytes]
0x18: unsigned long long  {ull}
0x20: float               {f}
Size: 36 bytes.

Class layout (64-bit, alignment member):
0x00: char                {c}
0x01: [alignment member, 3 bytes]
0x04: string              {str}
0x1C: short               {s}
0x1E: [alignment member, 2 bytes]
0x20: unsigned long long  {ull}
0x28: float               {f}
Size: 44 bytes.

Class layout (32-bit, reordered):
0x00: string              {str}
0x10: unsigned long long  {ull}
0x18: float               {f}
0x1C: short               {s}
0x1E: char                {c}
Size: 31 bytes.

Class layout (64-bit, reordered):
0x00: string              {str}
0x18: unsigned long long  {ull}
0x20: float               {f}
0x24: short               {s}
0x26: char                {c}
Size: 39 bytes.
// z.cpp
#include <cmath>

int func(int x, int y) {
    return static_cast<int>(round(pow((2 * x) - (3 * y), x + y) - (x / y)));
}

// -----

// y.cpp
int func(int x, int y) { return x + y; }

// -----

// x.cpp
int q = func(9, 11);
// Compiler has a heart attack, call 911.
// i.cpp
int global_int;

namespace Globals {
    int ns_int = -5;
}

// -----

// j.cpp
int global_int;

namespace Globals {
    int ns_int = 5;
}