是否可以在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 功能 初始化 C++ 推迟 const 是否 变量      更新时间:2023-10-16

在Java中,我们可以声明一个空白的final变量,并在以后对其进行初始化。编译器将确保初始化只发生一次 - 初始化失败或双重初始化都是编译时错误。例如:

public int bar() {
   return 66;
}
public void foo() {
    final int x; // declare the variable
    ...
    x = bar(); // initialization only once
}
在 Java 中,编译器可以保证x在第一次赋值之前绝对不会在任何代码路径上赋

值,并且可以保证绝对不会在任何代码路径上第二次赋值。(有关更多信息,请参见 Java 语言规范的第 16 章 "明确赋值"。

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

除非定义常量,否则无法初始化常量。你必须找到一种方法来知道它的价值在哪里被定义。如果x的值难以确定,请考虑使用函数的结果,例如

const int x = calc_x();

或像这样的闭包

const int x = []() { /* code to calculate x's value */ }();

const ness 是对象类型的一部分,对象类型在任何情况下都无法更改,因此要么x const,以后无法初始化它,要么根本不const x

可以设计一个模拟此内容的包装器class,但您最多只能得到运行时错误。

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

C++没有

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

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.
}