繁体   English   中英

一定义规则警告

[英]One definition rule warning

我被令人讨厌的“单一定义规则”违规所困扰。 我现在害怕在我的项目中有很多细微的错误。

例如,以下程序将导致 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
}

这编译没有警告。

这很糟糕,因为Source2.cpp甚至不使用Source2.cpp中的任何Source1.cpp 如果我从项目中删除Source1.cpp ,它仍然可以编译,不再有问题。

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

我有一些类,例如PointSerieStateItem ……我虽然这在小的 cpp 文件中没问题,但我意识到它不安全。

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

如果没有,避免ODR违规的最佳做法是什么?

这基本上就是我们拥有命名空间的原因。

每个软件组件使用一个众所周知的命名空间(例如booststdasiosqlmytoolyourlib等)。

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

namespace X {
  struct S {};
}

namespace Y {
  struct S {};
}

struct S {};

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

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

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

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

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

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

  • 使用强大的namespace组织类/结构名称并不难,即使数百万代码也是如此。 不要忘记namespace可以定义嵌套级别

  • 如果你真的想要一些“本地”定义,请尝试匿名namespace

  • 我记得如果程序员违反ODR,标准明确不需要任何诊断,所以依靠自己。

cppcheck将检测 ODR 违规,并且可以集成到 Visual Studio .

这是使用 cppcheck ver 2.6 查找 ODR 的快速命令行调用:

cppcheck classes/config/foo.cpp

输出:

classes/config/foo.h:15:1: error: The one definition rule is violated, different classes/structs have the same name 'MyClass' [ctuOneDefinitionRuleViolation]
class MyClass
^
classes/config/foo.h:60:1: note: The one definition rule is violated, different classes/structs have the same name 'MyClass'
class MyClass
^
classes/config/foo.h:15:1: note: The one definition rule is violated, different classes/structs have the same name 'MyClass'
class MyClass
^

请注意,正确捕获所有 ODR 需要能够立即查看完整的应用程序源——完整的程序分析。 因此,cppcheck 和其他静态分析器可能会给出误报或遗漏一些违规行为。 然而,这是寻找可能有问题的程序结构的一种简单的 FOSS 方法。

暂无
暂无

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

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