Does const "this" pointer implies constness of all its members? - c++

I know that if we add a "const" qualifier after a function declaration, it implies the implicit "this" pointer become const so we cannot directly modify any member
However, in a const member function, does every member automatically become const as well?
To illustrate what I'm trying to ask, here is a demo program
#include <vector>
struct Foo {
int a;
int b;
};
class Demo {
public:
Demo() = default;
void bar(std::vector<Foo>::iterator it) const {}
void baz() const {
// begin() is supposed to return iterator, not const_iterator
bar(vector.begin());
}
private:
std::vector<Foo> vector;
};
int main(void)
{
Demo d;
d.baz();
return 0;
}
Inside baz() method, I'm calling std::vector::begin() method, which according to doc, can either return a iterator or const_iterator
iterator begin() noexcept;
const_iterator begin() const noexcept;
My understanding is that, if member "vector" is declared as const, e.g.
private:
const std::vector<Foo> vector;
Then the overloaded form that returns a const_iterator shall be invoked. Otherwise, the overloaded form that returns a mutable iterator shall be invoked, which is the current case
When I try to compile it
g++ --std=c++17 -Wall
I got a compile error
main.cpp:17:13: error: no viable conversion from '__wrap_iter<std::__1::vector<Foo, std::__1::allocator<Foo> >::const_pointer>' to
'__wrap_iter<std::__1::vector<Foo, std::__1::allocator<Foo> >::pointer>'
bar(vector.begin());
^~~~~~~~~~~~~~
which essentially vector.begin() returns a const_iterator rather than iterator.
Now I'm confused, I do not declare vector as const, why the overloaded form to return const_iterator is still invoked?
Is it because baz() is declared as const? I understand that in a const member function, all memory directly associated with current object shall not be modified, but I don't expect this to cause such impact on function overloading. I expect vector.begin() to return a const_iterator if and only if vector is declared as a const member, e.g.
private:
const std::vector<Foo> vector;
Thanks in advance

Like everybody has said, if this is const then the members of whatever this points to are const.
To add on, you can have a member variable be non-const in a const function or context if you declare it with the mutable keyword... if you want that kind of behavior.
class Demo {
public:
Demo() = default;
void bar(std::vector<Foo>::iterator it) const {}
void baz() const
{
// begin() now returns iterator instead of const_iterator
bar(vector.begin());
}
private:
mutable std::vector<Foo> vector; // vector is non-const in const functions
};

Related

Returning iterator from constant member function

In the following code why the return type of foo::func is vector<int>::const_iterator and not vector<int>::iterator though I am returning an object of vector<int>::iterator.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class foo
{
private:
vector<int> ar;
public:
foo()
{
ar.resize(10);
iota(ar.begin(), ar.end(), 1);
}
auto func() const
{
return ar.begin() + 5;
}
};
int main()
{
foo x;
cout<<boolalpha<<endl;
auto it = x.func();
cout<<is_same<decltype(it), vector<int>::iterator>::value<<endl;
cout<<is_same<decltype(it), vector<int>::const_iterator>::value<<endl;
return 0;
}
Output of above code is :
false
true
Instead if I redefine foo::func() as
auto func()
{
return ar.begin() + 5;
}
The output will be
true
false
Why the constant member function is changing return type to constant?
And should I need to remove const keyword to make the return type as vector<int>::iterator or is there any other way?
A member function declared const affects the type of the this pointer. Within func(), this has type const foo*. As a result, all member types accessed via a const this pointer will themselves be const, so the implicit type of ar is in fact const std::vector<int>. Of the two begin() overloads for vector, the only viable overload is the const overload, which returns a const_iterator.
When you redefine func() to be non-const, then the type of this is simply foo*, so the type of the member ar is std::vector<int> and the overload of begin() returning iterator is preferred.
There are two std::vector::begin depending on whether the instance is const or not.
In foo::func() const, you are trying to access ar which is then viewed as a const std::vector<int>, and std::vector::begin() const returns a const_iterator.
When you access ar in foo::func(), it is then viewed as a std::vector<int>, without const, and begin will then refer to std::vector::begin() (without const again), which is not the same function.
Similarly, your own foo class could define two versions of foo::func simultaneously:
auto func()
{ return ar.begin() + 5; }
auto func() const
{ return ar.begin() + 5; }
And the constness of the instances will decide which version to call:
foo x;
const foo y;
x.func(); // First version, get an iterator
y.func(); // Second version, get a const_iterator
The iterator points to one of its data members and so if the function is const then the iterator must be const too otherwise you could change the data contained in the class from the iterator returned by a const member function.
That's my understanding at least - I would be grateful if someone else could confirm or deny this.
Member function begin is overloaded the following way
iterator begin() noexcept;
const_iterator begin() const noexcept;
Thus functions that are declared with qualifier const deal with constant objects. That means that data members of a constant object if they are not declared as mutable are also constant. In this case the second overloaded function begin is called for data member ar that returns const_iterator.

Return reference to a vector member variable

I have a vector as member in a class and I want to return a reference to it through a getVector() function, so as to be able to modify it later. Isn’t it better practice the function getVector() to be const? However I got an error “qualifiers dropped in binding reference of type…” in the following code. What should be modified?
class VectorHolder
{
public:
VectorHolder(const std::vector<int>&);
std::vector<int>& getVector() const;
private:
std::vector<int> myVector;
};
std::vector<int> &VectorHolder::getVector() const
{
return myVector;
}
Since it is a const member function, the return type cannot be non-const reference. Make it const:
const std::vector<int> &VectorHolder::getVector() const
{
return myVector;
}
Now it is okay.
Why is it fine? Because in a const member function, the every member becomes const in such a way that it cannot be modified, which means myVector is a const vector in the function, that is why you have to make the return type const as well, if it returns the reference.
Now you cannot modify the same object. See what you can do and what cannot:
std::vector<int> & a = x.getVector(); //error - at compile time!
const std::vector<int> & a = x.getVector(); //ok
a.push_back(10); //error - at compile time!
std::vector<int> a = x.getVector(); //ok
a.push_back(10); //ok
By the way, I'm wondering why you need such VectorHolder in the first place.
it's not unusual to declare both const and mutable variants, like so:
std::vector<int>& VectorHolder::getVector() {
return myVector;
}
const std::vector<int>& VectorHolder::getVector() const {
return myVector;
}
the underlying problem with your program is that you return a non-const reference from a const method.
std::vector<int>& VectorHolder::getVector() const {
return myVector; // << error: return mutable reference from const method
}
so you make it const using this form:
const std::vector<int>& VectorHolder::getVector() const {
return myVector; // << ok
}
and when this is in a non const method or the client holds a non-const reference, then you can legally use a non-const method:
std::vector<int>& VectorHolder::getVector() {
return myVector; // << ok
}
finally, you could return a value (in some cases):
std::vector<int> VectorHolder::getVector() const {
return myVector; // << ok
}
because the copy requires no mutation and provides no exposure to the internal data.
so you will end up declaring both variants quite often.
the results of declaring both are:
VectorHolder m;
const VectorHolder c;
m.getVector().size(); // << ok
c.getVector().size(); // << ok - no mutation
m.getVector().push_back(a); // << ok
c.getVector().push_back(a); // << error: attempt to mutate const reference because the const vector is returned
so it all works out nicely (apart from the redundancy of the methods).
The function getVector can be declared as const. It returns a reference that can be modified, so while the actual function doesn't modify anything in the class, the caller will be able to modify internal data.
Declare it as:
std::vector<int>& getVector();
If you want a function to return a vector that can't be modified, the use the const modifier on both the vector and the function:
const std::vector<int>& getVector() const;
The reason is that a const member function should only return const references. This is because in a const function, every data member becomes constant.
Therefore you have to declare the getVector() this way:
std::vector<int> &VectorHolder::getVector() const;

C++ compiler picking the wrong overload of a class member function

I have this code:
template <class T>
class Something
{
T val;
public:
inline Something() : val() {}
inline Something(T v) : val(v) {}
inline T& get() const { return val; }
inline Something& operator =(const Something& a) { val = a.val; return *this; }
};
typedef Something<int> IntSomething;
typedef Something<const int> ConstIntSomething;
class Other
{
public:
IntSomething some_function()
{
return IntSomething(42);
}
ConstIntSomething some_function() const
{
return ConstIntSomething(42);
}
};
void wtf_func()
{
Other o;
ConstIntSomething s;
s = o.some_function();
}
However, the compiler picks the wrong overload of Other::some_function() in wtf_func() (i.e. the non-const one). How can I fix this? Note that for certain reasons I cannot change the name of Other::some_function().
o is not const-qualified, so the non-const some_function is selected. If you want to select the const-qualified overload, you need to add the const qualifier to o:
Other o;
Other const& oref(o);
ConstIntSomething s;
s = oref.some_function();
When overload resolution occurs, the compiler only looks at the o.some_function() subexpression; it does not look at the context around the function call to decide to pick something else. Further, the return type of a member function is not considered during overload resolution.
Note that it may make more sense for IntSomething to be implicitly convertible to ConstIntSomething, either using an operator ConstIntSomething() overload in IntSomething (less good) or using a non-explicit ConstIntSomething(IntSomething const&) constructor in ConstIntSomething (more good).
It doesn't pick the wrong overload; const-ness is resolved by whether this is const or not. In your case, o is non-const, so the non-const overload is picked.
You can hack this by creating a const-reference to o, e.g.:
const Other &o2 = o;
s = o2.some_function();
But really, you should probably be considering your overloads in Something. For instance, you can't currently do this:
IntSomething x;
ConstIntSomething y;
y = x;
which doesn't sound correct. Why shouldn't you be allowed to take a const ref to a non-const ref?
Your object o needs to be const object for a const function to be called on it. Otherwise the compiler rightly picks up the non const version of the function.
The compiler picks the overload to use based on the constness of the object that will become this. You can make it call the desired version with static_cast: s = static_cast<const Other&>(o.some_function());
You might also want to copy the new behaviour found in the containers of the C++0x standard library. Containers such as vector now have members cbegin() and cend() that return a const_iterator whether the container is const or not unlike begin() and end()
class Other {
// Rest of other
public:
// No overload for non-const
// Even if called with a non const Other, since this member is marked
// const, this will be of type Other const * in all cases and will call
// the const qualified overload of some_function.
ConstIntSomething csome_function() const
{
return some_function();
}
};

Returning const 'this' pointer

if this is a const pointer to class's object how can you return a const pointer from non-const return type?
Class T
{
public:
T* func(){return this;}
};
Firstly, this is not a "const pointer". Where did you get that strange idea? Pointer this has scalar type and is not an lvalue, meaning that it can't possibly be "const" or "non-const". Pointer this is an rvalue and it is non-modifiable.
Secondly, the code in your question is valid regardless of whether the pointer involved is const or not. For example, the following code is valid for exactly the same reason
int *const p = 0; /* a const pointer */
int *foo() {
return p; /* OK, no error here */
}
What is returned in this case is a completely independent copy of the original pointer value. It does not matter whether p is const or not - making a copy of const value does not in any way violate its constness.
This is exactly what's done in your code sample - you are returning a copy of this's value. Why would you even care whether this is const or not?
Unless the member function is const-qualified (e.g. T* func() const instead of just T* func()), this is of type T*.
this is not itself modifiable (so you can't assign something to this), but it's just a pointer to an object of type T like any other and can be used as such.
AndreyT provided the explanation and here is the reference.
From standard docs 9.3.2.1 The this pointer,
In the body of a non-static (9.3) member function, the keyword this is an rvalue 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*.
which explains the question that you asked and the comment you made to AndreyT.
Hope this one also helps.
A pointer is not a object, so it does not matter if the pointer is const when it is a return value.
Consider this:
int x;
int f() {
return x;
}
const int g() {
return x;
}
There's actually no difference between f() and g().
But it may make difference if you replace all "int" to some other name of a class.
"const" may prevent you from some error, e.g. when you don't write a copying constructor properly.
class T
{
int *x;
public:
T() {x = new int;}
void set_x(int n) {*x = n;}
int get_x() const {return *x;}
};
T a;
T f() {
return a;
}
const T g() {
return a;
}
int main() {
f().set_x(123); // The value is modified
// even though a return value is not a l-value.
g().set_x(123); // Compile Error
}
So, if you want to prevent modifying data of the object by referring to the return value, what you need is:
class T
{
public:
const T* func() const
{ return this; }
};

Compiler error for modifying a non-const object

#include "iostream"
#include "vector"
class ABC {
private:
bool m_b;
public:
ABC() : m_b(false) {}
ABC& setBool(bool b) {
m_b = b;
return *this;
}
bool getBool() const {
return m_b;
}
};
void foo(const std::vector<ABC> &vec) {
vec[0].setBool(true);
}
int main(int argc, char*argv[]) {
std::vector<ABC> vecI;
ABC i;
vecI.push_back(i);
foo(vecI);
}
When I compile it I get this error: passing const ABC as this argument of ABC& ABC::setBool(bool) discards qualifiers
Any ideas why this would happen since the object itelf is not a constant.
foo takes vec by reference-to-const, and you cannot change a const. So either remove the line that calls setBool, or if you really want to set the bool, change the argument type to std::vector&.
Or to be more strict about the implementation... You see, these two functions exist:
T& vector<T>::operator[](int);
T const& vector<T>::operator[](int) const;
When you call "vec[i]" on a const object, only the second one is valid, so it gets selected. But this overload obviously returns T const&, and that's the thing you can't change.
foo takes a const vector &, meaning that the contents of the vector cannot be modified. This, in turn, means that you are not allowed to call a non-const member function on any of the elements of vec, and setBoo.
The object probably is const. Although the vector contains ABCs and not const ABCs, the vector itself is const, so when you call operator[] on it, the const version of the operator is used. The const version of operator[] on a vector returns a vector::const_reference. So, from what I can tell you are trying to call a non-const method on a const ABC&.
As another workaround, you could store pointers to ABC objects in the vector and it will work fine - albeit at the extra cost of managing the cleanup. In this case you are not modifying the contents of the vector since the vector is only holding pointers.