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.
Related
This question is a follow-up of A question regarding the implementation of std::add_pointer
Under std::add_pointer
there is the following reference:
Otherwise (if T is a cv- or ref-qualified function type), provides the
member typedef type which is the type T.
Based on reading Non-static member functions: const-, volatile-, and ref-qualified member functions, my understanding is that a for a non-static member function with given cvand/or ref qualification,
a) the cv qualification of the function applies to the this pointer as well, within the scope of the function
b) the ref qualification of the function does not apply to the this pointer within the scope of the function
Given this, why is it that std::add_pointer cannot provide the member typedef type T* in the case of a non-static member function with cv or ref qualification?
Per [dcl.ptr]/4:
[ Note: Forming a pointer to reference type is ill-formed; see
[dcl.ref]. Forming a function pointer type is ill-formed if the
function type has cv-qualifiers or a ref-qualifier; see
[dcl.fct]. Since the address of a bit-field cannot be taken, a
pointer can never point to a bit-field. — end
note ]
The pointer-to-cv-qualified-function type you are imagining is actually nonexistent. Therefore, std::add_pointer cannot produce such a type :)
A non-static member function type cannot be formed. Such a thing does not exist.
struct T {
int func() const;
};
func does not have a type. You cannot ever use it as an expression on its own.
using mf = int (T::*)() const;
mf myfunc = &T::func;
mf is a pointer to member function type. It is not a function type; pointers to non-static members (including member functions) are data, not functions.
A cv or ref qualification on a member function does not qalify the function type, which does not exist, but the type of the implicit "this" parameter.
The paragraph you quote only applies to non-member or static member functions.
In the following C++ program, modifying a static data member from a const function is working fine:
class A
{
public:
static int a; // static data member
void set() const
{
a = 10;
}
};
But modifying a non-static data member from a const function does not work:
class A
{
public:
int a; // non-static data member
void set() const
{
a = 10;
}
};
Why can a const member function modify a static data member?
It's the rule, that's all. And for good reason.
The const qualifier on a member function means that you cannot modify non-mutable non-static class member variables.
By way of offering some rationalisation, the this pointer in a const qualified member function is a const type, and this is inherently related to an instance of a class. static members are not related to a class instance. You don't need an instance to modify a static member: you can do it, in your case, by writing A::a = 10;.
So, in your first case, think of a = 10; as shorthand for A::a = 10; and in the second case, think of it as shorthand for this->a = 10;, which is not compilable since the type of this is const A*.
According to the C++ Standard (9.2.3.2 Static data members)
1 A static data member is not part of the subobjects of a class...
And (9.2.2.1 The this pointer)
1 In the body of a non-static (9.2.1) 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*,...
And at last (9.2.2 Non-static member functions)
3 ... if name lookup (3.4) resolves the name in the id-expression to a
non-static non-type member of some class C, and if either the
id-expression is potentially evaluated or C is X or a base class of X,
the id-expression is transformed into a class member access expression
(5.2.5) using (*this) (9.2.2.1) as the postfix-expression to the
left of the . operator.
Thus in this class definition
class A
{
public:
static int a;
void set() const
{
a = 10;
}
};
the static data member a is not a subobject of an object of the class type and the pointer this is not used to access the static data member. So any member function, non-static constant or non-constant, or a static member function can change the data member because it is not a constant.
In this class definition
class A
{
public:
int a;
void set() const
{
a = 10;
}
};
the non-static data member a is an subobject of an object of the class type. To access it in a member function there is used either a member access syntax of this syntax is implied. You may not use a constant pointer this to modify the data member. And the pointer this is indeed has type const A * within the function set because the function is declared with the qualifier const. If the function had no the qualifier in this case the data member could be changed.
The thing is, that if a member function of a class A is const, then the type of this is const X*, and thereby prevents non-static data members from being altered (cf, for example, C++ standard):
9.3.2 The this pointer [class.this]
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 a is a non-static data member, then a=10 is the same as this->a = 10, which is not allowed if the type of this is const A* and a has not been declared as mutable. Thus, since void set() const makes the type of this being const A*, this access is not allowed.
If a is a static data member, in contrast, then a=10 does not involve this at all; and as long as static int a by itself has not been declared as const, statement a=10 is allowed.
The const qualifier on a member function means that you cannot modify non-mutable, non-static class data members.
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.
I wrote this code in C++:
class Foo
{
public:
int& fun(){return var;} // 1st fun
int fun() const {return var;} // 2rd fun
private:
int var;
};
int main()
{
Foo foo;
int i = foo.fun();
return 0;
}
I know that C++ cannot discriminate overloading function by return value,but why when I added a const to 2rd function ,overloading can work ?
What the 'const' have done ?
Compiler cannot discriminate by return type because return values can undergo conversion before the assignment is performed. The object on which the function is invoked, on the other hand, is a parameter (albeit an implicit one) to the function, so the compiler can discriminate on it.
Const is used in the following way:
Foo inst1;
const Foo inst2;
inst1.fun(); // 1st fun
inst2.fun(); // 2nd fun
Const after the name of the function refers to the implicit this parameter. So, for inst1 it will Foo* and for inst2 const Foo*. This will guide the overload.
The return value is not used for selecting the overload. Methods/functions with the same set of params and different types of return value are not allowed on the same layer.
For what it's worth, the language from the standard (§13.3.1/3,4):
Similarly, when appropriate, the context can construct an argument list that contains an implied object
argument to denote the object to be operated on. Since arguments and parameters are associated by position within their respective lists, the convention is that the implicit object parameter, if present, is
always the first parameter and the implied object argument, if present, is always the first argument.
For non-static member functions, the type of the implicit object parameter is
— “lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier
— “rvalue reference to cv X” for functions declared with the && ref-qualifier
where X is the class of which the function is a member and cv is the cv-qualification on the member function declaration. [ Example: for a const member function of class X, the extra parameter is assumed to have type “reference to const X”. —end example ]
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.