Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is constness not enforced for pointers?

Consider the following code snippet:

class A
{
public:

    void nonConstFun()
    {

    }
};

class B
{
private:

    A a_;
    A * pA_;

public:

    void fun() const
    {
        pA_->nonConstFun();
        //a_.nonConstFun(); // Gives const related error
    }
};

int main()
{
    B b;
    b.fun();
}

Here I am expecting the compiler to fail the compilation for lack of constness for calling A::nonConstFun() inside B::fun() irrespective of the type of A object.

However the compiler complains for the object, but not for the pointer. Why? I am using VS2017 on Windows 10.

like image 237
Arun Avatar asked Oct 02 '18 11:10

Arun


2 Answers

The other answers explain the T* const vs T const * which is what is happening. But it is important to understand the implication of this beyond just the mere syntax.

When you have a T* inside a structure the pointer is inside the object (part of the layout of the objet), but the pointed object is physically outside of the structure. That is why a const object with a T* member is not allowed to modify the pointer, but it is allowed to modify the pointed object - because physically the pointed object is outside the enclosing object.

And it is up to the programmer to decide if the pointed object is logically part of the enclosing object (and as such should share constness with the enclosing) or if it is logically an external entity. Examples of former include std::vector, std::string. Examples of the latter include std::span, std::unique_ptr, std::shared_ptr. As you can see both designs are usefull.

The shortcoming of C++ is that it doesn't offer an easy way to express a logical constness as stated above (what you actually expected from your code).

This is known and for this exact purpose there is an experimental class which is not yet standard propagate_const

std::experimental::propagate_const is a const-propagating wrapper for pointers and pointer-like objects. It treats the wrapped pointer as a pointer to const when accessed through a const access path, hence the name.

struct B
{
    A a_;
    std::experimental::propagate_const<A *> pA_;

   void fun()
    {
        pA_->nonConstFun(); // OK
    }
    void fun() const
    {
        // pA_->nonConstFun(); // compilation error
    }
};
like image 173
bolov Avatar answered Nov 09 '22 16:11

bolov


It is enforced.

If you try changing the pointer, the compiler will not let you.

The thing that the pointer points to, however, is a different conversation.

Remember, T* const and T const* are not the same thing!

You can protect that by either actually making it A const*, or simply by writing your function in the manner that is appropriate.

like image 42
Lightness Races in Orbit Avatar answered Nov 09 '22 16:11

Lightness Races in Orbit