I know a const method cannot modify the object from which it is called. Look at this code:
class A{
int a;
public:
void f(A & a_) const {
a_.a=5;
};
};
int main(){
A x;
x.f(x);
return 0;
}
Why does this code compile? Why can I even assign a reference to a non const object of the same class, when declaring the method as constant? In general how can the compiler check all the possible situations in which the function could modify the object?
I know a const method cannot modify the object from which it is called.
This is an oversimplification, and slightly inaccurate.
A const function merely means that the implicit this pointer is a pointer to const.
Why does this code compile?
Because it is well-formed.
Why can I even assign a reference to a non const object of the same class, when declaring the method as constant?
Because constness of the function does not affect what objects you can modify through a reference.
In general how can the compiler check all the possible situations in which the function could modify the object?
The compiler simply does not make such checks.
A const member function cannot modify the object from which it is called using the "implicit" this parameter. f(...) is (ignoring member visibility) equivalent to the free function
void f(const A* this, A& _a) {
_a.a = 5;
}
If you pass the same object as a non-const pointer or reference, you are still allowed to modify it.
Related
If I have a class with getters of return type both const and non-const, how can I use the non-const one?
The class (MeshManipulator.h):
namespace vortex::meshSculptEngine
{
class MeshManipulator
{
public:
...
topologyMesh::Mesh* getMesh();
const topologyMesh::Mesh* getMesh() const;
...
};
}
The code where I want to use the Mesh (SimpleMeshCorrector.cpp):
void SimpleMeshCorrector::smoothBoundaryLoops(float distance)
{
if (!_boundaryLoops.empty()) {
Mesh* mesh = getEngine()->getManipulator()->getMesh();
...
}
}
I get the following error:
A value of type "const vortex::topologyMesh::Mesh*" cannot be used to
initialize an entity of type "vortex::topologyMesh::Mesh*"
I can use const_cast but I just don't feel that this is the correct way to solve this problem.
Root cause is (as clarified in comments) that getManipulator() returns a const MeshManipulator*. On a const object, only methods declared const can be called. Thus, the const getter is always called and never the non-const version.
Depending on the context, there are three ways to go on:
Use (or write) a method that returns a mutable MeshManipulator*.
Copy the const Mesh* to a local, mutable object.
Use const_cast<Mesh*> to be able to mutate the object in place. This, however, defeats the purpose of constness. What's more, if the object pointed to was declared const this will result in undefined behavior. So use with care.
Today I found out that code like that works. That sounds really strange to me, because as far as I always knew you can't modify any of members from const member function. You actually can't do it directly, but you can call non-const member function. if you mark member function as const that means that this pointer passed to the function is pointing to const object, then how non-const member function is called in the example bellow?
#include <iostream>
class X
{
public:
void foo() const
{
ptr->bar();
}
void bar() {}
private:
X * ptr;
};
int main()
{
}
Your member variable is not X, but pointer to X. As long as foo does not modify the pointer, it can be const.
When you have a pointer member, then the pointer is const in a const method. You won't be allowed to change the address stored in the pointer. But you can change the pointee all you like.
It's the difference between
X* const cannot_change_pointer; //this is how top-level const applies to pointers
const X* cannot_change_pointee;
What's even more interesting is that const on a method has no effect whatsoever on reference members for the same reason (a const method would only prevent you making a reference refer to something else which can't be done with a reference anyway).
That's seems perfectly OK. The call to foo does not modify the ptr member. Hence, the constness of foo is respected.
my 2 cents
If I have the class member function:
bar* foo::get_value() const
{
return &_value;
}
the compiler will complain
cannot initialize a variable of type bar * with an rvalue of type const bar *
_value is not const in the class.
If I have the function:
bar* foo::get_value() const
{
return const_cast<bar *>(&_value);
}
It works just fine.
I thought that declaring a member function constant meant that you promise to not modify the internal contents of the class, but the operation & seems to return a constant pointer in const member functions. Why is this?
When you declare a function const, you are saying that this is a function that is permitted on both a non-const and const instance of the object. Because it can be invoked on a const instance, the function should not mutate the object. When you return a non-const reference to an internal object, you are providing the caller with a way of mutating the object's internal state indirectly. To fix this, return a constant object, instead.
In other words, instead of:
Bar* Foo::GetBar() const
Do:
const Bar* Foo::GetBar() const
Or, better yet:
const Bar& Foo::GetBar() const
As you've observed, const_cast allows you to undermine the const-ness of the object. In general, you should avoid using const_cast; using it is a code smell, and it undermines the intended const gurantees.
When an object instance is declared const, it's treated as if all its member variables were also const -- that is, except members which have been declared with the keyword mutable.
So, theoretically, you could declare _value as mutable, which would at least be slightly preferable to const_cast<>-ing your way round it. But it's still not ideal. The right way to do this is to provide two overloads:
const bar* foo::get_value() const
{
return _value;
}
bar* foo::get_value()
{
return _value;
}
(and better yet would be to use references or smart pointers rather than raw pointers).
The problem is the return type of the getter function. You are returning bar* instead of const bar*. The former would allow the caller of the get_value function to modify the object. But that's explicitly forbidden by your declaration of _value as const.
You can bypass that with const_cast, but it's incorrect. It's like telling the compiler "trust me, I know what I'm doing", but in this case, you don't. You have no control over what the calling code will do with the object. And if they try to modify it—bam, undefined behavior.
Make life simple on yourself, just make the getter return const bar*:
const bar* foo::get_value() const
{
return &_value;
}
Do note that the compiler error message essentially told you the change you need to make to your code.
A const member function works only on a const object. With a const object you cannot change its member variables. That is why the compiler does not allow you to make a non const pointer to a member variable of a const object.
Today I found out that code like that works. That sounds really strange to me, because as far as I always knew you can't modify any of members from const member function. You actually can't do it directly, but you can call non-const member function. if you mark member function as const that means that this pointer passed to the function is pointing to const object, then how non-const member function is called in the example bellow?
#include <iostream>
class X
{
public:
void foo() const
{
ptr->bar();
}
void bar() {}
private:
X * ptr;
};
int main()
{
}
Your member variable is not X, but pointer to X. As long as foo does not modify the pointer, it can be const.
When you have a pointer member, then the pointer is const in a const method. You won't be allowed to change the address stored in the pointer. But you can change the pointee all you like.
It's the difference between
X* const cannot_change_pointer; //this is how top-level const applies to pointers
const X* cannot_change_pointee;
What's even more interesting is that const on a method has no effect whatsoever on reference members for the same reason (a const method would only prevent you making a reference refer to something else which can't be done with a reference anyway).
That's seems perfectly OK. The call to foo does not modify the ptr member. Hence, the constness of foo is respected.
my 2 cents
This question already has answers here:
Closed 11 years ago.
Possible Duplicates:
What is the meaning of a const at end of a member function?
about const member function
I found one function prototype as under:
const ClassA* ClassB::get_value() const
What does the above statement signify? Can I change the member of ClassA object?
The first const means what it returns is a pointer to const A. So no, you can't change what it returns (unless you cast away the const-ness, which will give undefined behavior if the object it returns is actually defined as const, rather than returning a const pointer to an object that itself wasn't defined as const).
The second const means that get_value can't change any of the (non-mutable) state of the ClassB on which it's invoked (among other things, it's transitive, so ClassB::get_value can only call other member functions that are also const-qualified).
No.
The ClassA pointer returned by that function is marked const. That means that you should not change any of its values.
It won't be impossible to change the values because there are various ways to get around a const marking, but you are clearly not meant to be changing it.
What does the above statement signify? Can i change the member of ClassA object.
get_value is a const member function of ClassB so it cannot modify any non-mutable data members of ClassB inside its definition. But it can however modify members of ClassA
For example the following compiles (leaks memory but that is not much of a concern here)
struct A{
int x;
};
struct B
{
const A* get_value() const
{
A *p= new A;
p->x = 12;
return p;
}
};
get_value() is a read-only function that does not modify the ClassB object for which it is called. It returns a read-only pointer to a ClassA object. You can modify the object pointed to by this object by casting away its constness using const_cast. But the ideal thing to do is to make a copy of this object and mutate that.