【问题标题】:One definition rule warning一定义规则警告
【发布时间】:2016-11-17 15:56:38
【问题描述】:

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

例如,以下程序将导致 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 甚至没有使用来自Source1.cpp 的任何东西。如果我从项目中删除Source1.cpp,它仍然可以编译,并且没有问题了。

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

我有一些课程,例如PointSerieStateItem,...虽然这在小型 cpp 文件中是可以的,但我意识到这并不安全。

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

【问题讨论】:

  • 在匿名命名空间中定义它们。

标签: c++ visual-studio visual-c++


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

    如果不是,避免违反 ODR 的最佳做法是什么?

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

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

    名称的命名空间实际上是其名称的一部分,因此如下:

    namespace X {
      struct S {};
    }
    
    namespace Y {
      struct S {};
    }
    
    struct S {};
    

    导致定义三个不同的类。一个叫X::S,一个叫Y::S,一个叫S,也叫::S

    ::全局命名空间。避免在此处声明名称是个好主意,因为您在程序中使用的任何 C 组件(或幼稚编写的 C++ 组件)都会很快用它们自己的名称污染这个命名空间。

    【讨论】:

      【解决方案3】:
      • 使用强大的namespace 来组织类/结构名称并不难,即使有数百万个代码。不要忘记namespace 可以定义嵌套级别

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

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

      【讨论】:

        【解决方案4】:

        cppcheck 将检测 ODR 违规,可以是 integrated into 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 requires the ability to see the full application source at once--完整程序分析。因此,cppcheck 和其他静态分析器可能会给出误报或遗漏一些违规行为。然而,这是寻找可能有问题的程序结构的一种简单的 FOSS 方法。

        【讨论】:

          猜你喜欢
          • 2014-03-16
          • 1970-01-01
          • 2017-10-13
          • 2021-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-09
          相关资源
          最近更新 更多