一个定义规则警告

Thr*_*r57 7 c++ visual-studio visual-c++

我被一个令人讨厌的"一个定义规则"违规所困扰.我现在害怕在我的项目中有很多微妙的错误.

例如,以下程序将导致使用visual studio 2015的空指针取消引用:

Source1.cpp:
----------
struct S {
    double d = 0;
};

void Foo() {
    S s;
}


Source2.cpp:
-----------
struct S {
    int a = 0;
};

int main() {

    int value = 5;
    int& valueRef = value;
    S s;           // valueRef is erased due to S::d initialization from Source1.cpp

    valueRef++;    // crash
}
Run Code Online (Sandbox Code Playgroud)

编译时没有任何警告.

这是令人讨厌的,因为Source2.cpp甚至没有使用任何东西Source1.cpp.如果我Source1.cpp从项目中删除它仍然编译,并且没有问题了.

在大型项目中,似乎很难确保没有cpp文件"本地"定义具有已定义名称的结构或类.

我有一些类,如Point,Serie,State,Item,...我虽然这是小cpp文件确定,但我意识到这不是安全的.

是否有编译器警告来捕获此类错误?如果没有,避免ODR违规的最佳做法是什么?

AnT*_*AnT 6

在这种特殊情况下,最底层的 ODR 违规(实际上会导致您观察到的问题)是class的隐式定义的内联构造函数S。您的程序有两个不匹配的内联S::S()函数版本,这可以看作是由原始 ODR 违规引起的另一个 ODR 违规(即相同的类定义不同)。

在当前的 C++ 编译基础结构方法中,实现很难“看到”这个错误。当然,只要付出足够的努力,就可以做到。

在这种情况下,为了使错误“可见”,您可以将类构造函数显式声明和定义为具有空主体的非内联函数。两个非内联的存在S::S()将触发链接器错误。

可以理解的是,您可能认为这是一种过于人为的措施,在某些情况下是不可接受的,因为它可能会改变类的“聚合”状态。


Ric*_*ges 5

如果不是,有什么最佳实践可避免违反ODR?

从根本上讲,这就是我们拥有名称空间的原因。

使用每个软件组件知名命名空间(例如booststdasiosqlmytoolyourlib等)。

名称的名称空间实际上构成其名称的一部分,因此以下内容:

namespace X {
  struct S {};
}

namespace Y {
  struct S {};
}

struct S {};
Run Code Online (Sandbox Code Playgroud)

导致定义了三个不同的类。一个被称为X::S,一个被称为Y::S,另一个S被称为::S

::全局名称空间。避免在此处声明名称是一个好主意,因为您在程序中使用的任何C组件(或天真的编写的c ++组件)都将使用其自己的名称快速污染此命名空间。