一个定义规则警告
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
甚至没有使用Source1.cpp
的任何东西。如果我从项目中删除Source1.cpp
,它仍然可以编译,并且不再有问题。
在大型项目中,似乎很难确保没有cpp文件"本地"定义具有已定义名称的结构或类。
我有一些类如Point
, Serie
, State
, Item
,…我认为这是OK的小cpp文件,但我意识到这是不安全的。
是否有编译器警告来捕获此类错误?如果没有,避免违反ODR的最佳实践是什么?
在这个特殊的例子中,在最底部的ODR冲突(它实际上导致了您正在观察的问题)是S
类的隐式定义的内联构造函数。你的程序有两个不匹配的内联S::S()
函数版本,这可以看作是由原始ODR冲突引起的另一个ODR冲突(即相同的类定义不同)。
在当前的c++编译基础结构中,实现很难"看到"这个错误。当然,只要足够努力,这是可以做到的。
在这种情况下,为了使错误"可见",可以显式地声明并定义类构造函数为空体的非内联函数。存在两个非内联S::S()
将触发链接器错误。
可以理解的是,您可能会认为这是一个过度人为的度量,在某些情况下是不可接受的,因为它可能会改变类的"聚合"状态。
如果没有,避免违反ODR的最佳实践是什么?
这就是命名空间存在的根本原因。
每个软件组件使用一个知名的命名空间(例如boost
, std
, asio
, sql
, mytool
, yourlib
等)。
名称的命名空间实际上构成了其名称的一部分,因此如下:
namespace X {
struct S {};
}
namespace Y {
struct S {};
}
struct S {};
导致定义三个不同的类。一个叫X::S
,一个叫Y::S
,另一个叫S
,也叫::S
。
::
是全局命名空间。避免在这里声明名称是一个好主意,因为您在程序中使用的任何C组件(或天真编写的c++组件)都会很快用自己的名称污染这个命名空间。
-
使用强大的
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和其他静态分析程序可能会给出误报或错过一些违规行为。然而,这是一种寻找可能有问题的程序结构的简单的自由/开源软件方法。
- 此代码是否违反一个定义规则
- 静态结构和一个定义规则
- 为什么这不违反单一定义规则?
- 一个定义规则 - 编译
- 了解 DCL60-CPP 中的不合规代码示例:遵守单一定义规则
- 为什么传递给函数 set::itrator 而不是 const_iterator 违反了一个定义规则?
- 内联函数和一个定义规则
- 如何在C 中应用ODR(一个定义规则)
- 我们如何添加Coverity工具的自定义规则
- 为什么这不违反一个定义规则
- 为什么没有放弃 C++17 的单一定义规则?
- 从标准库重新定义函数是否违反了一个定义规则
- 是否使用“ __Date__”或“ __Time__”违反了一个定义规则
- 非静态数据成员和一个定义规则
- 在代码(C/C++)中强制执行自定义规则
- C++:同名的文件范围常量违反了一个定义规则
- 一个定义规则是否强制创建一个静态函数变量
- 全局std::字符串和一个定义规则
- 默认模板参数是否进入单定义规则
- C 是否有一个像 C++ 这样的定义规则