请允许我先说明,出于显而易见的原因,我不推荐以下任何做法。然而,今天我进行了一次关于此事的讨论,一些人坚称使用像这样的参考作为未定义行为。
这是一个测试案例:
#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();
}
https://wandbox.org/permlink/YEp9aQUcWdA9YiBI
基本上,这段代码的作用是为结构体保留静态内存,然后在main()函数中对其进行初始化。为什么要这样做呢?它并没有太大的用处,你应该只使用指针,但问题是:有了这个语句,
constexpr my_object &I = my_object_storage_instance.memory;
定义一个未初始化内存的引用是否属于未定义行为?其他人告诉我是这样的,但我正在尝试确定是否确实如此。在C++标准中,我们看到了这段话:
引用必须初始化为引用有效对象或函数。[注:特别是,空引用不能存在于定义良好的程序中,因为创建这样的引用的唯一方法是将其绑定到通过解除引用空指针获得的“对象”,这会导致未定义行为。
具体来说,是指“有效对象”,这可能归结为:还没有调用构造函数的对象是否“有效”?什么使它无效以导致未定义行为?是否真的会出现实际的副作用?
我认为这被标记为未定义行为的理由是:
编译器可能会将其视为有效对象,因为标准规定应该这样做,特别是在赋值期间,尤其是如果插入了用于诊断的隐藏调试指令,这些指令假设它是有效的对象,这肯定会导致未定义行为。我反对它被视为未定义行为的论点是:
它没有解引用任何东西——该段落说明,在引用初始化期间,解引用nullptr是未定义的。如果没有任何解引用,它并没有明确说明未定义行为。
悬空引用是一种存在于许多正常程序中的情况。它们只有在使用时才会导致未定义行为。这类似于从一个悬空引用开始。
再次强调,实际上不太有用,因为有更好的方法来利用时间,但比stack overflow更适合奇怪的问题和专家意见的地方又在哪里呢? :)
std::optional
。 - Ben Voigt