是否存在未定义行为的有效“用例”?

时间:2013-05-01 12:53:35

标签: c++11 undefined-behavior

我找到了一段有UB的代码,并被告知要将其保留在代码中,并注明其为UB。仅使用MSVC2012。

代码本身有一个Foo个对象的原始数组,然后使用char*将该数组转换为reinterpret_cast<char*>,然后调用delete casted_array(就像这样,不删除[] )就此而言。

像这样:

Foo* foos = new Foo[500];

char* CastedFoos = reinterpret_cast<char*>(foos);

delete CastedFoos;

根据标准5.3.5 / 3,这显然是未定义的行为。

显然,这段代码的功能是避免将析构函数作为优化调用。

我想知道,实际上是否存在将代码中的UB视为有效的地方?

另外,就我而言,在代码中留下上述内容并不聪明,我是对的吗?

2 个答案:

答案 0 :(得分:7)

这完全取决于你的观点。

举一个极端的例子:在C ++ 03中,线程是未定义的行为。只要有多个线程,程序的行为就不再由C ++标准定义。

然而,大多数人会说线程有用

当然,根据C ++标准,多线程可能是UB,但是个别编译器并没有视为未定义。它们提供了额外的保证,即多线程将按照您的预期工作

在抽象地谈论C ++时,UB没有任何用处。怎么可能呢?你不知道会发生什么或者会发生什么。

但是在特定应用程序中,特定编译器编译的特定代码在特定操作系统上运行时,您有时可能知道一块UB是(1)安全的,(2)最终会产生某种有益效果。 / p>

答案 1 :(得分:6)

C ++标准定义了“未定义的行为”,如下所示:

  

此标准没有要求的行为

因此,如果您希望代码可以移植到不同的编译器和平台,那么您的代码不应该依赖于未定义的行为,因为在这些情况下,程序(由编译代码的不同编译器生成的程序)可能会有所不同。

如果您不关心可移植性,那么您应该检查您的编译器是否记录了它在感兴趣的情况下的行为。如果它没有记录它的作用(并且不必记录),请注意编译器可以在不同版本之间没有警告的情况下改变它的作用。还要注意其行为可能是不确定的。因此,例如它可能会在1%的时间内崩溃,您可能在临时测试中没有注意到,但是在它投入生产时会回来并咬你。因此,即使您使用的是一个编译器,依赖于未定义的行为仍然是一个坏主意。

关于您的具体示例,您可以重写它以实现相同的效果(不调用析构函数,但回收内存),不会导致未定义的行为。分配一个std::aligned_storage来保存Foo数组,调用 placement new 来构造aligned_storage上的Foo数组,然后当你想要释放数组时,释放{{1没有调用展示位置删除。

当然这仍然是一个糟糕的设计,可能会导致内存泄漏或其他问题,具体取决于aligned_storage应该做什么,但至少它不是UB。