引用未初始化的内存。未定义的行为?

muk*_*nda 7 c++ language-lawyer

首先,请允许我说,出于明显的原因,我不建议您采用以下任何一种做法。但是,我今天对此进行了讨论,有些人坚持使用像这样的引用作为未定义的行为。

这是一个测试案例:

#include <string>

struct my_object {
   int a          = 1;
   int b          = 2;
   std::string hi = "hello";
};

// Using union purely to reserve uninitialized memory for a class.
union my_object_storage {
   char dummy;
   my_object memory;
   // C++ will yell at you for doing this without some constructors.
   my_object_storage() {}
   ~my_object_storage() {}
} my_object_storage_instance;

// This is so we can easily access the storage memory through "I"
constexpr my_object &I = my_object_storage_instance.memory;

//-------------------------------------------------------------
int main() {
   // Initialize the object.
   new (&I) my_object();
   // Use the reference.
   I.a = 1;
   // Destroy the object (typically this should be done using RAII).
   I.~my_object();

   // Phase two, REINITIALIZE an object with the SAME reference.
   // We still have the memory allocated which is static, so why not?
   new (&I) my_object();
   // Use the reference.
   I.a = 1;  
   // Destroy the object again. 
   I.~my_object();
}
Run Code Online (Sandbox Code Playgroud)

https://wandbox.org/permlink/YEp9aQUcWdA9YiBI

基本上,代码会为结构保留静态内存,然后在main()中对其进行初始化。你为什么想这么做?它不是非常有用,您应该只使用一个指针,但这是一个问题:

有了这个陈述,

constexpr my_object &I = my_object_storage_instance.memory;

定义对未初始化的内存未定义行为的引用?其他人告诉过我,但我正在尝试具体弄清是否是这种情况。在C ++标准中,我们看到以下段落:

引用应初始化为引用有效的对象或函数。[注意:尤其是,空引用不能存在于定义良好的程序中,因为创建此类引用的唯一方法是将其绑定到通过取消引用空指针而获得的“对象”,这会导致未定义的行为。

具体来说,“有效对象”可以归结为:是一个尚未将其构造函数称为“有效”的对象吗?是什么使它会导致不确定的行为呢?实际上是否会出现真正的副作用?

我的观点这个被标记为未定义行为是:

  • 编译器可能会自由地将其视为有效对象,因为标准指出了它应该是有效的对象,特别是在分配过程中,尤其是如果为诊断插入了隐藏的调试指令(假设这样做),这肯定会导致不确定的行为。

反对它是未定义行为的说法是:

  • 它没有取消引用任何内容-该段指出,在引用的初始化期间,取消引用nullptr是未定义的。如果没有任何取消引用,它没有具体说明未定义的行为。
  • 悬挂引用是一回事,在正常程序中,在很多情况下都会出现。如果使用它们,它们只会导致未定义的行为。这类似于从悬挂参考开始。

再说一次,在实践中不是很有用,因为有更好的方法来度过您的时间,但是比stackoverflow哪个地方更适合提出奇怪的问题和专家意见?:)

Ben*_*igt 6

很好,引用的使用属于要求使用活动对象的规则的显式例外。在[basic.life]中

类似地,在对象的生存期开始之前但已分配了该对象将占用的存储空间之后,或者在对象的生存期结束之后以及该对象占用的存储空间被重用或释放之前,任何引用的glvalue可以使用原始对象,但只能使用有限的方式。

有关正在构造或销毁的对象,请参见[class.cdtor]。否则,此类glvalue会引用已分配的存储([basic.stc.dynamic.allocation]),并且明确定义了不依赖于glvalue的值的属性。该程序在以下情况下具有未定义的行为:

  • glvalue用于访问对象,或者
  • glvalue用于调用对象的非静态成员函数,或者
  • glvalue绑定到对虚拟基类([dcl.init.ref])的引用,或者
  • glvalue用作dynamic_­cast([expr.dynamic.cast])的操作数或的操作数typeid

如果在对象的生存期结束之后并且在重新使用或释放​​该对象占用的存储空间之前,在原始对象占用的存储位置上创建了一个新对象,则指向原始对象的指针,引用原始对象,否则原始对象的名称将自动引用新对象,并且在新对象的生命周期开始后,可以用于操作新对象,如果:

  • 新对象的存储空间正好覆盖了原始对象所占据的存储位置,并且
  • 新对象与原始对象具有相同的类型(忽略顶级cv限定词),并且
  • 原始对象的类型不是const限定的,如果是类类型,则不包含任何类型为const限定的非静态数据成员或引用类型,并且
  • 原始对象和新对象都不是潜在重叠的子对象([intro.object])。

因此,您的引用有效地指向分配的存储,这正是执行新放置并激活工会成员所需要的。

并且由于您创建的对象的动态(运行时)类型与您持有的引用的静态类型完全匹配,因此可以在放置新对象(第一个或第二个)之后使用该对象访问新对象。