我被一个讨厌的"一个定义规则"违反。 我现在害怕在我的项目中有很多微妙的错误。
例如,以下程序将导致使用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
,...我虽然在小cpp文件中可以,但我意识到了#39;不安全。
是否有编译器警告来捕获此类错误? 如果没有,避免ODR违规的最佳做法是什么?
答案 0 :(得分:4)
如果没有,避免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 ++组件)都会使用自己的名称快速污染此命名空间。
答案 1 :(得分:3)
在这种特殊情况下,最底层的ODR违规(实际导致您正在观察的问题)是类S
的隐式定义的内联构造函数。您的程序有两个不匹配的内联S::S()
函数版本,可以看作是原始ODR违规引起的另一个ODR违规(即同一个类定义不同)。
实施很难实现"见"当前C ++编译基础结构方法中的这个错误。当然,可以做足够的努力。
在这种情况下,为了使错误"可见"您可以显式声明并将类构造函数定义为具有空主体的非内联函数。存在两个非内联S::S()
将触发链接器错误。
可以理解的是,您可能会认为这是一种过于人为的措施,在某些情况下是不可接受的,因为它可能会改变"聚合"班级的地位。
答案 2 :(得分:3)
使用强大的namespace
组织类/结构名称并不难,即使数百万代码也是如此。不要忘记namespace
可以定义嵌套级别
如果你真的想要一些定义"本地",请尝试匿名namespace
我记得如果程序员违反ODR,标准明确不需要任何诊断,所以依靠自己。