Why is it impossible to convert const X to X &? - c++

I'm trying to understand and achieve const correctness on a Tetris Project.
Here is a recurrent problem that i have when i'm trying to add const where I think it's necessary.
I have a (Piece) class, and one of it's class private member is
Point rotationCenter;
And I'm trying to write a getter like this:
inline Point & Piece::getRotationCenter() const
{
return rotationCenter;
}
Before, I had the same getter, but not as a const function, and was working. Now, I got the C2240 error "impossible to convert const Point to Point &".
What should I do to correct this? Should I leave getRotationCenter without const ?
PS : I read https://isocpp.org/wiki/faq/const-correctness as tutorial.

Why is it impossible to convert const X to X &?
Because if it is allowed, the following dangerous code becomes valid:
const int x = 0;
int& rx = x; // bind const variable to reference (to non-const)
rx = 99; // oops, try to modify the const variable via the reference
What should I do to correct this? Should I leave getRotationCenter without const ?
It depends on your intent. If the returned object could be modified, then make the member function non-const and return Point&. If not, then leave the member function const and make the return type const Point&. A const member function means a promise that won't modify (or provide the possibility to modify) the object (and its members).

Inside const member functions all of the classes data members are const. You cannot bind a non-const reference to your const member data, so you get a compiler error.
If you don't want your caller to modify rotationCenter, then you can return by Point or const Point&.
inline const Point & Piece::getRotationCenter() const
{
return rotationCenter;
}
If you do want the caller to modify rotationCenter (which I would generally not recommend), write two overloads: one which returns by Point& and one which returns by const Point& depending on the qualification of the object you call it on:
inline Point & Piece::getRotationCenter() //not const
{
return rotationCenter;
}
inline const Point & Piece::getRotationCenter() const
{
return rotationCenter;
}

Related

On the weak semantics of references-to-const (and pointers-to-const)

This has probably been already asked.
Why is it allowed to assign a reference-to-const to a non-const variable?
Why is this allowed
int mut {0};
const int & r_to_c {mut};
mut = 1;
// now r_to_c changed to 1!
// But it was supposed to be a reference to something constant!
?
Sure, I cannot mutate the value from the reference-to-const itself. I cannot
r_to_c = 2;
but isn't the const qualification enforcing too little? I would expect, from a promise of const-ness, that binding to mutable variables was disallowed.
Otherwise what guarantees is const giving me? They seem pretty weak, and it seems that this could easily trick programmers to shoot themselves in their foot.
I know that C++ has a reputation for allowing people to shoot themselves in their foot. I don't have a problem with allowing dangerous things. In this case, my problem is that in this case it seems that it is purposefully deceiving, given that the semantics of const here is not the one would expect it.
Mine is a question about the compiler and the language semantics, not about references in particular (I could have asked the same question using a pointer-to-const that is assigned to the address of a non-const variable. Like int mut{0}; const int * p_to_c{&mut};).
Why is the semantics of a reference-to-const (or pointer-to-const) just "you can't use this particular window to modify the thing you see (but if you have other windows that are non-const, you can modify it)" instead of a more powerful "this can only be a window to something that was declared constant and that the compiler guarantees it stays constant"?
[Note on terminology: I use the expression "reference-to-const" instead of "const reference" because a "const reference", interpreted as T& const - consistently with calling T* const a "const pointer" -, does not exist.]
but isn't the const qualification enforcing too little? I would expect, from a promise of const-ness, that binding to mutable variables was disallowed.
No it is not "too little". You are expecting the wrong thing.
First, whether you bind a const reference does not make the object itself const. That would be strange:
void foo(int& x) {
static const int& y = x;
}
When I call foo:
int x = 42;
foo(x);
I cannot know whether somebody else will keep a const reference to my non-const x.
Otherwise what guarantees is const giving me?
You cannot modify something via a const reference:
void bar(const int& x);
int x = 0;
bar(x);
When I call a function that takes a const& then I know that it will not modify my (non-const) parameter. If const references would not bind to non-const objects then there would be no way to make this last example work, i.e. you could pass non-const objects only to functions that do modify them, but not to functions that do not modify them.
P.S. I can understand your confusion. It is sometimes overlooked that holding a constant reference does not imply that the object cannot be modified. Consider this example:
#include <cstddef>
#include <iostream>
struct foo {
const int& x;
};
int main() {
int y = 0;
foo f{x};
std::cout << f.x; // prints 0
y = 42;
std::cout << f.x; // prints 42
}
Printing the value of the member to the screen yields two different results, even though foo::x is a constant reference! It is a "constant reference" not a "reference to a constant". What const actually means here: You cannot modify y through f.x.
The ability to bind a const-reference to a mutable variable is actually a very valuable feature to have in the language. Consider that we might want to have a mutable variable.
int mut {0};
// ... some time later
mut = 1;
This is perfectly reasonable; it's a variable that is going to change during the execution of the program.
Now let's say we want to print the value of this variable, and would like to write a function to do that.
void print(int param) // or 'int &' to avoid a copy,
// but the point here is that it's non-const
{
std::cout << param;
}
This is fine, but clearly the function is not changing the parameter. We would like that to be enforced so that mistakes like param = 42; are caught by the compiler. To do that, we would make param a const & parameter.
void print(int const & param);
It would be quite unfortunate if we couldn't call this function with arguments that are non-const. After all, we don't care that the parameter might be modified outside the function. We just want to say that the parameter is guaranteed not to be modified by print, and binding a const & to a mutable variable serves exactly that purpose.
A reference to a const object is not the same as a const reference to a non const object, but C++'s type system does not distinguish them.
This is sort of a violation of the LSP; it has the same kind of problem as a reference to a mutable square and rectangle do.
You can create "true const" but you need help at declaration.
template<class T>
struct true_const {
const T value;
};
a true_const<int>& or true_const<T> const& can be passed around as a reference, and nobody can edit it (without invoking UB) "behind your back".
Of course, a function taking a true const cannot also take a normal object.
void bob( true_const<int>& x ) {
auto local = x.value;
call_some_other_function();
assert(local == x.value); // guaranteed to be true
}
void bob( const int& x ) {
auto local = x;
call_some_other_function();
assert(local == x); // NOT guaranteed to be true
}
const fields in classes are truly const; modifying them is undefined behavior.
A thin wrapper around a type that is const within the class is thus a guarantee the data is const. Then take a reference to that.
Now, the true_const could use some operator support.
template<class T>
struct true_const {
const T value;
constexpr T const& get() const { return value; }
constexpr T const& operator*() const { return get(); }
constexpr T const* operator->() const { return std::addressof(value); }
constexpr operator T const&() const { return get(); }
// concepts-defended operator+,==, etc
};

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;

What does the const keyword do in an operator definition?

I don't understand what the const keyword is used for in front of the return type and after the parameter list of this operator definition. This is taken from an example from a book.
const char& operator [] (int num) const
{
if (num < getlength())
return Buffer[num];
}
The C++ const keyword basically means "something cannot change, or cannot delegate operations that change onto other entities." This refers to a specific variable: either an arbitrary variable declaration, or implicitly to this in a member function.
The const before the function name is part of the return type:
const char&
This is a reference to a const char, meaning it is not possible to assign a new value to it:
foo[2] = 'q'; // error
The const at the end of the function definition means "this function cannot change this object and cannot call non-const functions on any object." In other words, invoking this function cannot change any state.
const char& operator [] (int num) const {
this->modifySomething(); // error
Buffer.modifySomething(); // error
return Buffer[num];
}
The goal of const-correctness is a big topic, but the short version is being able to guarantee that immutable state actually is immutable. This helps with thread safety and helps the compiler optimize your code.
It means the actual object you are calling this method on will not change, and if you attempt to change it, you'll get a compiler error.

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;

C++ Difference between const positioning

I'm struggling to get my head around the differences between the various places you can put 'const' on a function declaration in c++.
What is the difference between const at the beginning:
const int MyClass::showName(string id){
...
}
And const at the end like:
int MyClass::showName(string id) const{
...
}
Also, what is the result of having const both at the beginning and at the end like this:
const int MyClass::showName(string id) const{
...
}
const int MyClass::showName(string id) returns a const int object. So the calling code can not change the returned int. If the calling code is like const int a = m.showName("id"); a = 10; then it will be marked as a compiler error. However, as noted by #David Heffernan below, since the integer is returned by copy the calling code is not obliged to use const int. It can very well declare int as the return type and modify it. Since the object is returned by copy, it doesn't make much sense to declare the return type as const int.
int MyClass::showName(string id) const tells that the method showName is a const member function. A const member function is the one which does not modify any member variables of the class (unless they are marked as mutable). So if you have member variable int m_a in class MyClass and if you try to do m_a = 10; inside showName you will get a compiler error.
Third is the combination of the above two cases.
The const attached to the return value applies to the return value. Since the return value is copied it's a pointless declaration and it makes no difference whether or not you include it.
The const after the parameter list means that the function does not modify any state of the object that is not marked as mutable. This is a const member function and if you have a const object the compiler will not allow you to call non-const member functions on a const object.
There is no interaction between these two uses of const - they are completely independent constructs
The difference is that the const applies to different things.
This says that showName returns a constant int value -- one that is immutable. Of course since the int is returned by value the presence of const here does not play any role.
const int MyClass::showName(string id)
And this says that showName does not modify the observable state of MyClass (technically: it does not modify any member that is not declared mutable), and therefore you are allowed to call it on a value of type const MyClass.
int MyClass::showName(string id) const
If you use both consts then both of the above apply.
Questions like this tend to strengthen my resolve to follow the advice of Dan Saks as outlined in this "Conversations with a Guru" article: http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835 .
In particular it deals with how the placement of const changes things. (*) I realize that I am extremely unlikely to convert anyone from writing const int ... to int const ..., but that said, there is one reason I prefer to do the latter.
(*) incuding a note that swapping the const with the type declaration at the very start is the one change that has no effect.
It makes for very easy readability, because for any instance of the word
const in a declaration, everything to the left of it is the type of what is const, and everything to the right is what actually is const.
Consider a declaration like:
int const * * const pointerToPointer;
The first const states that the integers at the end of the pointer chain are const, and they are to be found at * * const pointerToPointer. Meanwhile the second one states that an object of type pointer to pointer to const int is also const and that this object is pointerToPointer.
In the OP's case:
int const MyClass::showName(string id){
...
}
The type of what is const is int, and what is const is the return value from the function.
Meanwhile:
int MyClass::showName(string id) const {
...
}
Here the type of what is const is function(string) returning int, and what is const is the function itself, i.e. the function body.