Constness of template member in const member function - c++

How is const applied to a template member in a const member function? I found the following to be interesting (this is in VS15):
class TcpSocket;
class TcpThread
{
TcpSocket* Listener() const;
std::vector< TcpSocket* > sockets_;
};
TcpSocket* TcpThread::Listener() const
{
auto s = sockets_.front();
return s;
}
I added the auto to clarify what was going on. It is deduced as TcpSocket*, so the non-const version of front is being selected. However, if I insert
sockets_.erase(sockets_.begin());
as the first line of code, it fails to compile, essentially saying that sockets_ is const.
It makes sense for it to work as it does, but there is evidently more going on here than simply "treat each member as const in a const member function.

sockets_ inside Listener is const. Let's have a look at what front returns:
reference front();
const_reference front() const;
So we'll get a const_reference, in this case a TcpSocket * const&.
This is where your expectation is incorrect. Stripping away the reference for sake of clarity, you expect a const TcpSocket*, it gives you a TcpSocket * const. The former is a pointer to a const TcpSocket, the latter is a const pointer to a TcpSocket.
So what front gives you is a pointer which you can't change to a TcpSocket which you can change.
As such, it's perfectly valid to make a non-const copy of this pointer with its pointee available for modification:
auto s = sockets_.front();
//sockets_.front() returns TcpSocket* const
//s copies it to a TcpSocket*

It's not that the non-const version of front is called, it's just that you're storing pointers, and then you're putting it into auto which always deduces by-value (and not by reference--for which you need auto& =). Because you're copying the const pointer, you then have your own copy, and so const is omitted for it, unless you explicitly define it that way. That's why you're deducing TcpSocket* instead of TcpSocket* const.
If you want to verify this, try doing auto& s = _sockets.front() and see what type you get then.
Note. too, that since you're storing a pointer, that vector::const_reference you'd get back would be point to a const pointer and not a pointer to const.
The container itself, being const in that scope, means that you can't change its sequence of elements, or what they point to. So you can't say _sockets.erase() nor can you say _sockets[0]. However, since the elements themselves are pointers to a non-const TcpSocket, that means that you can do pretty much whatever you want with them. It's the container you can't fiddle with.

Even though std::vector< TcpSocket* > sockets_; is itself const, the containee (i.e. TcpSocket*) is not const.
That's why you get the non-const deduction.

Why you do not get TcpSocket * const:
Although front() returns TcpSocket* const &, auto is deduced to be TcpSocket*.
Consider:
double const & foo();
// ...
double const a = 4.0;
auto b = a; // valid, decltype(b) === double
double c = a; // valid, too
double d = foo(); // valid
auto e = foo(); // decltype(e) === double
You're making a copy anyway, so why would that copy be const?
You do not get anything valueable from having s being TcpSocket * const.
Why you do not get TcpSocket const *:
That's just because you store TcpSocket* in the vector. The constness of front() makes sure that the stored pointer is not altered (i.e. you cannot make sockets_.front() point to another TcpSocket from within Listener()) but maintining const-correctness of the object pointed to is due to the user.

Related

Why is it when "this" is a pointer to const, the return type has to be a constant reference and not a simple reference? [duplicate]

Why won't the method getRanks() below compile, and how can I fix it gracefully?
All I want do is define a member accessor method that returns a reference to a member. The reference is not const since I might well modify what it refers to later. But since the member method does not modify the object, I declare it const. The compiler (clang, std=c++11) then insists that there is a "binding of reference" that "drops qualifiers". But I'm NOT dropping qualifiers, am I? And if I am, why:
struct teststruct{
vector<int> ranks;
vector<int>& getRanks()const{
return ranks;
}
};
Now, the code compiles if I change the return statement to cast away the const:
return const_cast<vector<int>&>(ranks);
But "ranks" should not be const in the first place, I don't see why I need to const_cast the const away. I don't even know if it's safe to do this.
Anyway, is there a cleaner to write this method? Can someone explain why such a simple common-sense method fails? I do want to declare the getRanks() method "const" so that I can call it from other const methods.
The idea behind the const member function is you should be able to call them on const objects. const functions can't modify the object.
Say you have a class
class A
{
int data;
void foo() const
{
}
};
and on object and a function call:
A const a;
a.foo();
Inside A::foo, this->data is treated as if its type is int const, not int. Hence, you are not able to modify this->data in A:foo().
Coming to your example, the type of this->ranks in getRanks() is to be considered as const vector<int> and not vector<int>. Since, auto conversion of const vector<int> to vector<int>& is not allowed, the compiler complains when you define the function as:
vector<int>& getRanks()const{
return ranks;
}
It won't complain if you define the function as:
const vector<int>& getRanks()const{
return ranks;
}
since const vector<int> can be auto converted to const vector<int>&.
ranks is const because the enclosing object (*this) is const, so you have to return a reference to a std::vector<int> const.
If you want to allow the client to modify the vector (and thereby affecting the member), then the getter should not be const. Note that the getter is silly anyway, since ranks is already a public data member.
You are returning a reference to ranks, which is a member of teststruct. That means that anybody that gets this reference could modify the internals of the teststruct object. So the const is a lie.
Don't cast away the const. Instead, decide between whether you want the function to be const and return a copy of ranks or const reference, or to be non-const and return a mutable reference. You can always have both if necessary.
It drops qualifiers because you return a reference to ranks. Any code after can modify ranks. Ex:
auto v = teststruct.getRanks();
v[1] = 5; //assuming v.size() > 1
You can fix this by returning a copy:
vector<int> getRanks() const
Or a const reference:
const vector<int>& getRanks() const
If you want ranks changable even in a const object, you could do this:
mutable vector<int> ranks;

About const_cast usage in cppreference

Please refer to this page and go down to the Example.
This is a demonstration about usage of
struct type {
type() :i(3) {}
void m1(int v) const {
// this->i = v; // compile error: this is a pointer to const
const_cast<type*>(this)->i = v; // OK as long as the type object isn't const
}
int i;
};
const here means that m1 could not modify type's member variables. I can't understand why const_cast modify the constness of this. I mean, this pointer points to current type object, not the i itself.
Why not:
const_cast<int>((this)->i) = v;
May I say when one of member function use a const qualifier, the whole object and all member variables become const? Why this is const pointer?
I can't understand why const_cast modify the constness of this
It doesn't. You could say it creates another temporary pointer, which is not a pointer to a const type, but contains the same address as this. It then uses that pointer to access i.
Why not const_cast<int>((this)->i)
You could do something like this, but you need a cast to a reference, not a plain integer.
const_cast<int&>(i) = v; // this-> omitted for brevity
Same caveats apply as any modification of i if this points at an object that is really const.
May I say when one of member function use a const modifier, the whole object and all member variables become const? Why this is const pointer?
Yes. The const qualifier on the member function means that this's pointee type is type const. And all members (which are not mutable) are also const for any access via this.

Why can't a const method return a non-const reference?

Why won't the method getRanks() below compile, and how can I fix it gracefully?
All I want do is define a member accessor method that returns a reference to a member. The reference is not const since I might well modify what it refers to later. But since the member method does not modify the object, I declare it const. The compiler (clang, std=c++11) then insists that there is a "binding of reference" that "drops qualifiers". But I'm NOT dropping qualifiers, am I? And if I am, why:
struct teststruct{
vector<int> ranks;
vector<int>& getRanks()const{
return ranks;
}
};
Now, the code compiles if I change the return statement to cast away the const:
return const_cast<vector<int>&>(ranks);
But "ranks" should not be const in the first place, I don't see why I need to const_cast the const away. I don't even know if it's safe to do this.
Anyway, is there a cleaner to write this method? Can someone explain why such a simple common-sense method fails? I do want to declare the getRanks() method "const" so that I can call it from other const methods.
The idea behind the const member function is you should be able to call them on const objects. const functions can't modify the object.
Say you have a class
class A
{
int data;
void foo() const
{
}
};
and on object and a function call:
A const a;
a.foo();
Inside A::foo, this->data is treated as if its type is int const, not int. Hence, you are not able to modify this->data in A:foo().
Coming to your example, the type of this->ranks in getRanks() is to be considered as const vector<int> and not vector<int>. Since, auto conversion of const vector<int> to vector<int>& is not allowed, the compiler complains when you define the function as:
vector<int>& getRanks()const{
return ranks;
}
It won't complain if you define the function as:
const vector<int>& getRanks()const{
return ranks;
}
since const vector<int> can be auto converted to const vector<int>&.
ranks is const because the enclosing object (*this) is const, so you have to return a reference to a std::vector<int> const.
If you want to allow the client to modify the vector (and thereby affecting the member), then the getter should not be const. Note that the getter is silly anyway, since ranks is already a public data member.
You are returning a reference to ranks, which is a member of teststruct. That means that anybody that gets this reference could modify the internals of the teststruct object. So the const is a lie.
Don't cast away the const. Instead, decide between whether you want the function to be const and return a copy of ranks or const reference, or to be non-const and return a mutable reference. You can always have both if necessary.
It drops qualifiers because you return a reference to ranks. Any code after can modify ranks. Ex:
auto v = teststruct.getRanks();
v[1] = 5; //assuming v.size() > 1
You can fix this by returning a copy:
vector<int> getRanks() const
Or a const reference:
const vector<int>& getRanks() const
If you want ranks changable even in a const object, you could do this:
mutable vector<int> ranks;

Why is conversion from const pointer-to-const to const pointer-to-nonconst in an initializer list allowed

I read the question posted on
Why does C++ not have a const constructor?
I am still confused why that program can compile. And I tried to offer my opinion on the question, I don't know why it was deleted. So I have to ask the question again.
Here is the program
class Cheater
{
public:
Cheater(int avalue) :
value(avalue),
cheaterPtr(this) //conceptually odd legality in const Cheater ctor
{}
Cheater& getCheaterPtr() const {return *cheaterPtr;}
int value;
private:
Cheater * cheaterPtr;
};
int main()
{
const Cheater cheater(7); //Initialize the value to 7
// cheater.value = 4; //good, illegal
cheater.getCheaterPtr().value = 4; //oops, legal
return 0;
}
And my confusion is :
const Cheater cheater(7)
creates a const object cheater, in its constructor
Cheater(int avalue) :
value(avalue),
cheaterPtr(this) //conceptually odd legality in const Cheater ctor
{}
'this' pointer was used to initialize cheaterPtr.
I think it shouldn't be right. cheater is a const object, whose this pointer should be something like: const Cheater* const this; which means the pointer it self and the object the pointer points to should both const, we can neither change the value of the pointer or modify the object the pointer points to.
but object cheater's cheaterPtr member is something like Cheater* const cheaterPtr. Which means the pointer is const but the object it points to can be nonconst.
As we know, pointer-to-const to pointer-to-nonconst conversion is not allowed:
int i = 0;
const int* ptrToConst = &i;
int * const constPtr = ptrToConst; // illegal. invalid conversion from 'const int*' to 'int*'
How can conversion from pointer-to-const to pointer-to-nonconst be allowed in the initializer list? What really happened?
And here is a discription about "constness" in constructors I tried to offer to the original post:
"Unlike other member functions, constructors may not be declared as const. When we create a const object of a class type, the object does not assume its 'constness' until after the constructor completes the object's initialization. Thus, constructors can write to const objects during their construction."
--C++ Primer (5th Edition) P262 7.1.4 Constructors
If constructors were const, they couldn't construct their object - they couldn't write into its data!
The code you cite as "legal:"
cheater.getCheaterPtr().value = 4; //oops, legal
is not actually legal. While it compiles, its behaviour is undefined, because it modifies a const object through a non-const lvalue. It's exactly the same as this:
const int value = 0;
const int * p = &value;
*const_cast<int*>(p) = 4;
This will also compile, but it's still illegal (has UB).
Your assumptions are incorrect. Taking them one at a time, first code annotation.
class Cheater
{
public:
Cheater(int avalue) :
value(avalue),
cheaterPtr(this) // NOTE: perfectly legal, as *this is non-const
// in the construction context.
{}
// NOTE: method is viable as const. it makes no modifications
// to any members, invokes no non-const member functions, and
// makes no attempt to pass *this as a non-const parameter. the
// code neither knows, nor cares whether `cheaterPtr`points to
// *this or not.
Cheater& getCheaterPtr() const {return *cheaterPtr;}
int value;
private:
Cheater * cheaterPtr; // NOTE: member pointer is non-const.
};
int main()
{
// NOTE: perfectly ok. we're creating a const Cheater object
// which means we cannot fire non-const members or pass it
// by reference or address as a non-const parameter to anything.
const Cheater cheater(7);
// NOTE: completely lega. Invoking a const-method on a const
// object. That it returns a non-const reference is irrelevant
// to the const-ness of the object and member function.
cheater.getCheaterPtr().value = 4;
return 0;
}
You said:
I think it shouldn't be right. cheater is a const object whose this pointer should be something like: const Cheater* const this
cheater is const after construction. It must be non-const during construction. Further, the constructor does not (and cannot) know the caller has indicated the object will be const. All it knows is its time to construct an object, so thats what it does. Furthermore, after construction &cheater is const Cheater *. Making the actual pointer var itself also const is simply non-applicable in this context.
And then...
...object cheater's cheaterPtr member is something like Cheater* const cheaterPtr;
This is actually an incredibly accurate way to describe this. Because cheater is const its members are as well, which means the cheaterPtr member is const; not what it points to. You cannot change the pointer value, but because it is not a pointer-to-const-object you can freely use that pointer to modify what it points to, which in this case happens to be this.
If you wanted both the pointer and its pointed-to-object to be const you whould have declared it as const Cheater *cheaterPtr; in the member list. (and doing that, btw, makes only that mutable action through the getCheaterPointer() invalid. It would have to return a const Cheater* as well, which means of course the assignment would fail.
In short, this code is entirely valid. What you're wanting to see (construction awareness of the callers const-ness) is not part of the language, and indeed it cannot be if you want your constructors to have the latitude to... well, construct.

c++ const member function that returns a const pointer.. But what type of const is the returned pointer?

I apologize if this has been asked, but how do I create a member function in c++ that returns a pointer in the following scenerios:
1. The returned pointer is constant, but the junk inside can be modified.
2. The junk inside is constant but the returned pointer can be modified.
3. Neither the junk, nor the pointer can be modified.
Is it like so:
int *const func() const
const int* func() const
const int * const func() const
All of the tutorials I've read don't cover this distinction.
Side note:
If my method is declared const then the tutorials say that I'm stating that I won't modify the parameters.. But this is not clear enough for me in the case when a parameter is a pointer. Do my parameters need to be like:
a. void func(const int* const x) const;
b. void func(const int* x) const;
c. void func(const int* const x) const;
I don't know what book you have read, but if you mark a method const it means that this will be of type const MyClass* instead of MyClass*, which in its turn means that you cannot change nonstatic data members that are not declared mutable, nor can you call any non-const methods on this.
Now for the return value.
1 . int * const func () const
The function is constant, and the returned pointer is constant but the 'junk inside' can be modified. However, I see no point in returning a const pointer because the ultimate function call will be an rvalue, and rvalues of non-class type cannot be const, meaning that const will be ignored anyway
2 . const int* func () const
This is a useful thing. The "junk inside" cannot be modified
3 . const int * const func() const
semantically almost the same as 2, due to reasons in 1.
HTH
Some uses of const don't really make much sense.
Suppose you have the following function:
void myFunction (const int value);
The const tells the compiler that value must not change inside the function. This information does not have any value for the caller. It's up to the function itself to decide what to do with the value. For the caller, the following two function definitions behave exactly the same for him:
void myFunction (const int value);
void myFunction (int value);
Because value is passed by value, which means that the function gets a local copy anyway.
On the other hand, if the argument is a reference or a pointer, things become very different.
void myFunction (const MyClass &value);
This tells the caller that value is passed by reference (so behind the screens it's actually a pointer), but the caller promises not to change value.
The same is true for pointers:
void myFunction (const MyClass *value);
We pass a pointer to MyClass (because of performance reasons), but the function promises not to change the value.
If we would write the following:
void myFunction (MyClass * const value);
Then we are back int he first situation. myFunction gets a pointer, which is passed by value, and which is const. Since MyFunction gets a copy of the pointer value, it doesn't matter for the caller whether it is const or not. The most important thing is that myFunction can change the contents of value, because the pointer variable itself is const, but the contents in it isn't.
The same is true for return values:
const double squareRoot(double d);
This doesn't make any sense. squareRoot returns a const double but since this is passed 'by value', and thus needs to be copied to my own local variable, I can do whatever I want with it.
On the other hand:
const Customer *getCustomer(char *name);
Tells me that getCustomer returns me a pointer to a customer, and I am not allowed to change the contents of the customer.
Actually, it would be better to make the char-pointer-contents const as well, since I don't expect the function to change the given string:
const Customer *getCustomer(const char *name);
int *const func() const
You cannot observe the const here except for a few cases
Taking the address of func.
In C++0x, directly calling func with the function-call syntax as a decltype operand, will yield int * const.
This is because you return a pure pointer value, that is to say a pointer value not actually stored in a pointer variable. Such values are not const qualified because they cannot be changed anyway. You cannot say obj.func() = NULL; even if you take away the const. In both cases, the expression obj.func() has
the type int* and is non-modifiable (someone will soon quote the Standard and come up with the term "rvalue").
So in contexts you use the return value you won't be able to figure a difference. Just in cases you refer to the declaration or whole function itself you will notice the difference.
const int* func() const
This is what you usually would do if the body would be something like return &this->intmember;. It does not allow changing the int member by doing *obj.func() = 42;.
const int * const func() const
This is just the combination of the first two :)
Returning a pointer to const makes a lot of sense, but returning a const pointer (you cannot modify) usually adds no value (although some say it can prevent user errors or add compiler optimisation).
That is because the return value belongs to the caller of the function, i.e. it is their own copy so it doesn't really matter if they modify it (to point to something else). The content however does not "belong" to the caller and the implementor of the function may make a contract that it is read-only information.
Const member functions promise not to change the state of the class, although this is not necessarily enforced in reality by the compiler. I am not referring here to const_cast or mutable members so much as the fact that if your class itself contains pointers or references, a const member function turns your pointers into constant pointers but does not make them pointers to const, similarly your references are not turned into references-to-const. If these are components of your class (and such components are often represented by pointers) your functions can change their state.
Mutable members are there for the benefit of allowing your class to change them whilst not changing internal state. These can typically be applied to:
Mutexes that you wish to lock even for reading.
Data that is lazy-loaded, i.e. filled in the first time they are accessed.
Reference-counted objects: You want to increase the reference count if it has another viewer, thus you modify its state just to read it.
const_cast is generally considered a "hack" and is often done when someone else has not written their code properly const-correct. It can have value though in the following situations:
Multiple overloads where one is const and one non-const and the const returns a const-reference and the non-const returns a non-const reference, but otherwise they are the same. Duplicating the code (if it is not a simple data member get) is not a great idea, so implement one in terms of the other and use const_cast to get around the compiler.
Where you want in particular to call the const overload but have a non-const reference. Cast it to const first.
The const method prevents you from modifying the members. In case of pointers, this means you can't reassign the pointer. You can modify the object pointed at by the pointer to your heart's desire.
As the pointer is returned by value (a copy), the caller can't use it to modify the pointer member of the class. Hence adding const to the return value adds nothing.
Things are different if you were to return a reference to the pointer. Now, if the pointer weren't const, this would mean that a function that doesn't have rights to modify a value is granting this right to the caller.
Example:
class X
{
int* p;
public:
int* get_copy_of_pointer() const //the returned value is a copy of this->p
{
*p = 42; //this being const doesn't mean that you can't modify the pointee
//p = 0; //it means you can't modify the pointer's value
return p;
}
int* const& get_reference_to_pointer() const //can't return a reference to non-const pointer
{
return p;
}
};