Is there an efficient way to make reference to constants actually const instead of read only?

纵然是瞬间 提交于 2019-12-07 07:08:45

问题


Let's look at the following C++ code:

#include <iostream>

int main()
{
    int z = 2;

    class A {
        public:
        const int & x;
        A(const int & x) : x(x) {}
        void show(){
            std::cout << "x=" << this->x << std::endl ;
        }
    } a(z);

    a.show();
    z = 3;
    a.show();
}

The program prints: 2 and 3

It clearly shows that while inside class A x can't be modified, it merely means it's read only, because I can change it's value from outside.

Of course I can make it a copy stored inside class A, but I'm wondering if there is (or if there is a proposal?) of a way to say to class A that the member x will be truly constant instead of merely read only, with the meaning of a promise that the external code won't change it ?

To my eyes it looks like something related to the meaning of the C restrict keyword, but I've not heard of any such C++ feature yet. Do you ?


回答1:


Constness is an attribute of the actual variable.

The term const int& x simply means "x is a reference to an int which it will not modify" and of course the compiler enforces this.

If you want the actual variable to which x refers to be const, simply declare it so:

#include <iostream>

int main()
{
    const int z = 2;    // declared const. Nothing may ever modify it

    class A {
    public:
        const int & x;
        A(const int & x) : x(x) {}
        void show(){
            std::cout << "x=" << this->x << std::endl ;
        }
    } a(z);

    a.show();
    z = 3;        // this is a logic error, caught by the compiler.
    a.show();
}

compiling correctly produces the error:

./const.cpp:41:7: error: read-only variable is not assignable
    z = 3;
    ~ ^
1 error generated.



回答2:


You're looking for D's immutable keyword, which was introduced as a new concept in that language precisely because, unfortunately, the answer is no: it does not exist in C++.




回答3:


Constness in C++ does not mean immutability, but that the variable in question is read-only. It can still be modified by other parts of the program. I understand your question as to whether it's possible to enforce true immutability in a called function without knowing what the caller is doing.

Of course you can create a template wrapper class which accomplishes the task:

template <typename T>
class Immutable
{
public:
    template <typename ...Args>
    Immutable( Args&&...args ) 
        : x( std::forward<Args>(args)... )
    {}

    operator const T &() const
    {
        return x;
    }

private:
    const T x;
};

As long as you do not reinterpret_cast or const_cast you will have truly immutable objects when you wrap them with Immutable<T>.

However, if you have a constant reference to some object, there is no way to tell, if some other part of the program has a non-constant access to the object. In fact, the underlying object might be a global or static variable, that you have read-only access to, but functions you call might still modify it.

This cannot happen with Immutable<T> object. However, using Immutable<T> might impose an extra copy operation on you. You need to judge yourself if you can live with that and if the cost justifies the gain.

Having a function require an const Immutable<Something> & instead of const Something & as an argument affects the calling code. A copy operation might be triggered. Alternatively, you can ask for an Immutable<Something> & without the const. Then no accidental copies will be triggered, but the calling code must pass a reference to Immutable<Something> object. And rightly so, because if the caller received a const & as an argument then the caller does not know, whether the object might get modified by someone else in the program. The caller has to create the object itself or require an immutable object to be passed to it as a reference.

Your original question

Here's your original problem with Immutable<int> & instead of const int &.

#include <iostream>

int main()
{
    Immutable<int> z = 2;

    class A {
        public:
        const Immutable<int> & x;
        A(Immutable<int> & x) : x(x) {}
        void show(){
            std::cout << "x=" << this->x << std::endl ;
        }
    } a(z);

    a.show();
    //z = 3; // this would fail
    a.show();
}

An other example

Here's how it works: If you write

void printAndIncrementAndPrint( int & i1, const int & i2 )
{
    std::cout << i2 << std::endl;
    ++i1;
    std::cout << i2 << std::endl;
}

int main()
{
    int i = 0;
    printAndIncrementAndPrint( i, i );
}

then it will print

0
1

into the console. If you replace the second argument of printAndIncrementAndPrint() with const Immutable<int> & i2 and keep the rest the same, then a copy will be triggered and it will print

0
0

to the console. You cannot pass and Immutable<int> to the function and a int & to the same underlying data without breaking the typesystem using const_cast or reinterpret_cast.




回答4:


I think this is a design problem for the programmers, not the language. A const variable means for any user of that variable, they should not change the value of that variable. Our compiler is smart enough to help us make sure of that. So A is a user of z and if you want A know that A::x references to a const variable, then you should make z a const int. The const reference is just to keep the contract between the user and the provider.



来源:https://stackoverflow.com/questions/34509616/is-there-an-efficient-way-to-make-reference-to-constants-actually-const-instead

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!