Let X be a class with member function f().
this is an implicit argument for f(), it is of type X* const.
Then, if f() const is a const member function, the type for the this pointer is now const X* const.
In both cases, it seems that the type for the this pointer is const. Why then is it allowed inside the function f() definition to modify any data member of class X? Shouldn't we always resort to const_cast as in
void X::f() {
const_cast <int&> (member) = 1;
}
If f() const is const, then this is the way to do:
void X::f() const{
const_cast <int&> (member) = 1;
}
(or you can also have member mutable)
But why is this working
void X::f() {
member = 1;
}
this is implicit argument for f(), it is of type X* const.
Not quite (it's actually an rvalue of type X*), but close enough for the sake of this question.
In both cases, it seems that type for this pointer is const. Why then is it allowed inside the function f() definition to modify any data member of class X ?
Because if the pointer is const (as in X* const), you can't change the pointer. You can change whatever it points to. If it's a pointer-to-const (as in const X*), then you can't change what it points to.
So you can never modify this itself; you can't write this = &some_other_object. In a const member function, you also can't modify the (non-mutable) members of *this without a dodgy const_cast.
This pointer is a rvalue. It cannot be assigned a value, so that this pointer is a const pointer does not matter.
If f() is a non const member function, this pointer points to non const X, so data member of X can be modified.
But if f() const is a const member function, this pointer points to const const X, so data member of X cannot be modified. Then, it need for data member to be mutable or to const_cast if one wants to modify it inside f() const definition.
We can do, for const_cast,
void X::f() const{
const_cast <int&> (member) = 1;
}
If member is a reference to a non-const int, then assignment is accepted by compiler and have wanted behavior. If member is reference to a const int, behavior is not predictable. Compare these:
const int a = 10;
const int* b = &a;
int* c = const_cast<int*>(b);
// *c = 20;
c is a const_cast of a const int* whose pointee a is const. Behavior is undefined.
int a1 = 10;
const int* b1 = &a1;
int* c1 = const_cast<int*>(b1);
*c1 = 20;
c1 is a const_cast of a const int* whose pointee a is const. This is working, you can freely assign new value to int pointed by c1.
It is known that const member function cannot change value of data members. What we say her is that this is because then, this pointer's pointee is const.
Related
This question already has answers here:
Pointers and References as member variables of const objects
(2 answers)
Closed 5 years ago.
Why does this compile?
struct A {};
struct B {
B(A& _a) : a(_a) {}
A &a;
};
void f1(A&) {}
void f2(const B &b) { f1(b.a); }
int main() {
A a;
B b{a};
f2(b);
return 0;
}
Inside f2() b is const, so my understanding was that b.a should also be const. But it does compile and the compiler allows calling f1().
Replace 'A& a;' in struct B with 'A a;' and it no longer works.
Now in f1() b.a indeed is const:
invalid initialization of reference of type 'A&' from expression of type 'const A'
Please help me understand this... Thanks.
When an object is const, it doesn't cause the reference members to also become const since the referent is not a part of the object itself. The reference member is just a piece of information representing the address of some other object. Whether or not the B object itself is immutable shouldn't affect whether it should be possible to mutate the objects it references.
If you make the B::a member a non-reference, as in A a;, then the B object will actually contain within itself an A object, so when the former is const, the latter will be too.
Inside f2() b is const, so my understanding was that b.a should also be const.
It is. If the instance is const then it's members will be, too. But look at the type of the member:
A & a;
That's a reference to A. Making that const yields a constant reference to A:
A & const a;
Not a reference to a constant A.
Strictly speaking there are no constant references. There are references to constant objects.
In the class B the data member a is declared like a reference to non-constant object of the type A.
A &a;
and this reference is passed as an argument to a function that accepts a reference to a non-constant object
void f1(A&) {}
void f2(const B &b) { f1(b.a); }
Thus the code compiles successfully.
void testMethod(){
cout<<"Normal Method";
}
void testMethod() const{
cout<<"Const Method";
}
which of these will be called by call to testMethod()? When I tried the first was called.
But how is this decided and it can't always be the first, otherwise there is no point in treating these two as different functions.
If the member function is called through a reference to const or a pointer to const, or if it is called directly on an object whose type is const-qualified, the overload qualified as const will be picked. Otherwise, the overload not qualified as const will be picked.
X x;
x.testMethod(); // Calls the non-const version
X const& y = x;
y.testMethod(); // Calls the const version
X* z = &x;
z->testMethod(); // Calls the non-const version
X const w;
w.textMethod(); // Calls the const version
In more formal terms, paragraph 9.3.2/3 of the C++11 Standard specifies (in the quote, cv stands for const-or-volatile, and you can ignore the volatile part for the purposes of your question):
A cv-qualified member function can be called on an object-expression (5.2.5) only if the object-expression is
as cv-qualified or less-cv-qualified than the member function [...]
This question already has answers here:
Type of 'this' pointer
(4 answers)
Closed 9 years ago.
struct Foo
{
void f()
{
// (*)
}
};
What is the type of "this" in the line marked with (*) ?
Is it const Foo* or Foo* ?
n3376 9.3.2/1
In the body of a non-static (9.3) member function, the keyword this is a prvalue expression whose value
is the address of the object for which the function is called.
The type of this in a member function of
a class X is X*. If the member function is declared const, the type of this is const X*, if the member
function is declared volatile, the type of this is volatile X*, and if the member function is declared
const volatile, the type of this is const volatile X*.
Inside f, this has type Foo * because f is not a const member function.
You cannot call f on a const Foo object. The following is erroneous:
const Foo obj;
obj.f();
This is precisely because inside Foo::f, the this pointer is Foo * rather than const Foo *, and so the function call demands a pointer conversion which discards a qualifier.
The this pointer itself is not a variable. It is not assignable, but not because of a const qualifier. There is no declaration in scope such as Foo *const this. A this expression is simply not an lvalue, as a rule of the language.
The this pointer is not very different from &obj.
The type of this depends on the member function.
For example for a class X, if the member functions is
1) const, Then this is of type const X*
2) volatile, then this is volatile X* etc
otherwise it is X*
this refers to current object. The type of this in c++ is Foo *const.
I'm not clear on the impact that returning const values has on move semantics in C++11.
Is there any difference between these two functions, which return data members? Is const still redundant in C++11?
int GetValueA() { return mValueA; }
const int GetValueB() { return mValueB; }
What about for these functions?
int GetValuesAB() { return mValueA + mValueB; }
const int GetValuesCD() { return mValueC + mValueD; }
An expression calling a function that returns by value is a prvalue. However, there are no const prvalues of non-class non-array type (§5/6):
If a prvalue initially has the type “cv T,” where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.
This means that there's no difference between your two definitions of the function. Whether it returns a const int or just an int is irrelevant because the expression is never const.
However, there is a difference when you're returning a class type. Consider the following example:
struct foo
{
void bar() { std::cout << "Hello" << std::endl; }
};
foo get_foo();
Now, if we call get_foo(), we get a temporary foo object. This prvalue is not const and we can call non-const member functions on it, so we could happily do get_foo().bar(). However, we can change the declaration of get_foo like so:
const foo get_foo();
Now, the expression get_foo() is a const prvalue (which is allowed because it is a class type) and we cannot call bar on the temporary object returned by it any more.
Nonetheless, it doesn't make sense to talk about move semantics for a non-class type, as an int is never moved from. If you return a const class type, that can also not be moved from because it is const. To demonstrate:
foo get_foo();
foo f(get_foo()); // Will call the move constructor
const foo get_foo();
foo f(get_foo()); // Will call the copy constructor
This is because a const prvalue won't bind to a non-const rvalue reference, which is what the move constructor takes as its argument.
class A{
private:
int a;
public:
A() {a = 4;}
const int& random1() const {return a; }
//int& random2() const {return a; }
const int* random3() const {return &a;}
//int* random4() const {return &a;}
};
int main(){
A objA;
cout<<objA.random1()<<"\n";
cout<<*objA.random3()<<"\n";
}
random2() and random4() are not permitted as defined above. I somehow knew this all along but never came across it while writing my own code, until today.
What all except these two cases is not permitted in const member functions?
Any reference to C++ standard text will also be helpful. Thanks!
First understand that const T* is a pointer to some T that cannot be changed. The second thing to remember is all members are actually accessed via this->.
So (§9.3.1):
A nonstatic member function may be declared const, volatile, or const volatile. These cvqualifiers affect the type of the this pointer (9.3.2).
And what it does (§9.3.2):
In the body of a nonstatic (9.3) member function, the keyword this is a non-lvalue expression whose value is the address of the object for which the function is called. The type of this in a member function of a class X is X*. If the member function is declared const, the type of this is const X*, if the member function is declared volatile, the type of this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile X*.
A const on a function makes the this pointer const T*.
This is why those examples fail: In the int& variant, a is accessed as this->a, this is const T*, so a is a const int. And const int cannot be implicitly converted to int&. Same with the other function.
In other words, when a function is const it smacks a const on everything in the class, and you can't implicitly cast the const away.
Const member functions can't call non-const member function, even if they don't change any member data. Sometimes you need to provide both const and non-const versions of the same function, because this pointer is implicitly passed to member functions and plays a role in overload resolution.