是否可以延迟 C++ 中 const 变量的初始化,例如 Java 的 "blank final" 功能?

Is it possible to defer initialization of a const variable in C++, like Java's "blank final" feature?

在Java中我们可以声明一个blank final变量,稍后再初始化。编译器将确保初始化恰好发生一次——初始化失败或双重初始化都是编译时错误。例如:

public int bar() {
   return 66;
}

public void foo() {
    final int x; // declare the variable
    ...
    x = bar(); // initialization only once
}

在Java中,编译器可以保证x在第一次赋值之前绝对不会在任何代码路径上赋值,并且可以保证绝对不会在任何代码路径上进行第二次赋值代码路径。 (有关详细信息,请参阅 Chapter 16, Definite Assignment, of the Java Language Specification。)

我们如何在 C++ 中实现类似的行为?是否可以声明一个变量 const 但推迟其初始化? (不丢弃 const 说明符。)

除非定义了常量,否则无法对其进行初始化。您必须找到一种方法来了解其定义的价值。如果 x 的值难以确定,请考虑使用像

这样的函数的结果
const int x = calc_x();

或像

这样的闭包
const int x = []() { /* code to calculate x's value */ }();

constness 是对象类型的一部分,对象类型在任何情况下都不能更改,因此 xconst 并且您无法对其进行初始化稍后或 x 根本不是 const

可以设计一个包装器 class 来模拟这个,但你最多只能得到一个 运行 时间错误。

请注意,似乎有 const_cast 形式的解决方案,但假设所讨论的对象实际上不是 const。在 const int x 的情况下,无法在初始化后合法地更改它的值。

C++ 没有针对此的内置功能。不过,您可以自己构建它。您可以创建一个 class 来保存您想要的类型的对象的存储空间,并且可以为此重载赋值运算符,以便它只能被调用和初始化一次。那看起来像

template<typename T>
class once
{
private: 
    std::aligned_storage_t<sizeof(T), alignof(T)> data;
    T* ptr = nullptr;
public:
    once() = default;
    ~once()
    {
        if(ptr) // it is initialized so call the destructor
            ptr->~T();
        // optionally you can add
        // throw("a once<T> must be initialized once");
        // this can help to enforce that the object is actually initialized as you'll get a runtime exception in code that does not do so
    }
    template<typename U>
    once& operator =(U&& value)
    {
        if (!ptr) // it is not initialized so call constructor
        {
            ptr = new(&data) T(std::forward<U>(value));
        }
        else
            throw ("can only assign to a once<T> once.");
        return *this;
    }
    operator const T&()
    {
        return *ptr;
    }

};

然后你会像

一样使用它
int main()
{
    once<int> foo;
    if (1 < -1)
        foo = 21;
    else
        foo = 42;
    std::cout << foo;
    //foo = 23; // uncomment this to get an exception.
}