是否可以像Java的“空白最终”功能那样推迟C ++中const变量的初始化?

时间:2019-04-01 15:23:25

标签: java c++ c++11 const final

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

public int bar() {
   return 66;
}

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

在Java中,编译器可以保证绝对不会在第一次分配之前在任何代码路径上分配x,并且可以保证绝对不会在任何代码路径上第二次分配const。 (有关更多信息,请参见Chapter 16, Definite Assignment, of the Java Language Specification。)

我们如何在C ++中实现类似的行为?是否可以将变量声明为const但推迟其初始化? (无需丢弃final _auth = FirebaseAuth.instance; _auth.onAuthStateChanged.listen((user) { if (user != null) { //get useer data } else { //go to login page } }); Future<Null> _loginByFacebook(Completer completer) async { FacebookLogin facebookLogin = new FacebookLogin(); FacebookLoginResult result = await facebookLogin.logInWithReadPermissions(['email', 'public_profile']); switch (result.status) { case FacebookLoginStatus.loggedIn: AuthCredential credential = FacebookAuthProvider.getCredential(accessToken: result.accessToken.token); FirebaseAuth.instance.signInWithCredential(credential); completer.complete(); break; case FacebookLoginStatus.cancelledByUser: break; case FacebookLoginStatus.error: completer.completeError(result.errorMessage); } } Future<Null> _loginByGoogle( Completer completer) async { try { await GoogleSignIn().signIn(); completer.complete(); } catch (error) { completer.completeError(error); print(error); } } 说明符。)

2 个答案:

答案 0 :(得分:3)

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

之类的函数的结果
const int x = calc_x();

或类似

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

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

可以设计一个可以对此进行模拟的包装器class,但充其量您会遇到运行时错误。

请注意,似乎有一种const_cast形式的解决方案,但是它假定所讨论的对象实际上不是const。对于const int x,初始化后无法合法更改其值。

答案 1 :(得分:3)

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