What does the const keyword do in an operator definition? - c++

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.

Related

Difference between Const usage in functions in c++

I'm having the below method with multiple const keywords. why are they used?
const int* MyClass::getvalue(const int input) const
if returning a pointer from a method, what are the ways to restrict the user from changing the pointer value and pointer itself?
First of all, having the return type for a value (as opposed to a reference or a pointer) being const is rather useless. The caller of the function can still copy the value to a non-constant variable anyway. For a reference or a pointer, it means that the referenced/pointed to object can not be modified. It can still be copied to a non-constant object though.
The argument being const means that the function can not change the argument. It is partly informational, partly helps the compiler choose optimizations, and for references or pointers means that whatever is referenced/pointed to can't be modified. For references there's also the semantic that you can pass temporary objects to the function.
The last const is for the function itself, and makes it so that the function can only be called on const objects. If you have a non-const MyClass object, this function can't be called on it. This const is part of the function signature, together with the function name and its argument types. That means you can have two overloaded functions with the same name and arguments, one being const qualified and one not.
I'm having the below method with multiple const keywords. why are they used?
const int MyClass::getvalue(const int input) const
^
This makes the return value const. There is no reason to use a return by const value. Some compilers will warn if you use const here. Note that conversely, a reference or a pointer to const object would be reasonable.
const int MyClass::getvalue(const int input) const
^
This makes the argument const. Whether a value argument is const or not makes little difference. A minor advantage of const is that you can know from the declaration that the local object won't change throughout the function, which can be helpful if the function is complex.
const int MyClass::getvalue(const int input) const
^
This makes a member function const. This allows the member function to be called on const instances of the class, but also prevents the function from modifying non-mutable members of the object.
if returning a pointer from a method, what are the ways to restrict the user from changing the pointer value and pointer itself?
There is no way of restricting the user from changing the value of a pointer object that you've returned to them, and there is never a need to do so.
You can restrict the user from modifying the pointed object by returning a pointer to const.
The last const specifies that getvalue() won't change the instance of MyClass it is called upon.
const int input declares a parameter of type int to the function getvalue() specified as const. Its value cannot be changed inside the function.
In the return type const is quite meaningless since the returned value can be assigned to a non-const-qualified int without problem.
what are the ways to restrict the user from changing the pointer value and pointer itself?
Read declarations backwards:
int const * const foo; // foo is a constant pointer to a constant int
int * const bar; // bar is a constant pointer to a int
int const * qux; // qux is a pointer to a constant int

Why is it impossible to convert const X to X &?

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;
}

Return Pointer To Non-Modifiable Member Array C++ gcc warning

I would like to return a pointer to an array owned by a class. However, I do not want to allow users to modify that data or pointer. According to how I understand things you need return a constant pointer to constant data using the following syntax.
const Type *const Class::Method() {
return this->data_;
}
However gcc gives the following warning when compiling.
warning: type qualifiers ignored on function return type
Why is this warning provided by gcc? What does it mean? If this is not the right syntax for what I want, what is?
The warning you get is because the final const is overlooked. Take it off and you're set.
You don't need to return a const pointer to const data, just a pointer to const data (which is const Type*). Just as it doesn't make sense to return a const int, it doesn't make sense to return a T* const, because as soon as the value is assigned to a new variable, that const is discarded.
The top level const is ignored for built-in types. As there is a rule in C++[3.14p4]: class and array prvalues can have cv-qualified types; other prvalues always have cv-unqualified types.. In your case const Type* const, the top level const making the pointer const is ignored.
You could add const to the end: const Type * Class::Method() const {...}. This would prevent the pointer from being modified inside the member function. However, since the member function returns prvalue which is non-modifiable, it is not necessary to do this to prevent modification of the pointer member outside of the class (and this is also the reason why this rule exists in C++). It may be useful when you want to call the function with a constant reference to a Class object, etc., but for what you are doing, this doesn't seem necessary.
First const is right, the second const does not make any sense. Just look at this example:
const int foo();
int a = foo();
The fact if foo returns const int or just int does not change anything in this case. Same for Type *.

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.

Why does this const member function allow a member variable to be modified?

class String
{
private:
char* rep;
public:
String (const char*);
void toUpper() const;
};
String :: String (const char* s)
{
rep = new char [strlen(s)+1];
strcpy (rep, s);
}
void String :: toUpper () const
{
for (int i = 0; rep [i]; i++)
rep[i] = toupper(rep[i]);
}
int main ()
{
const String lower ("lower");
lower.toUpper();
cout << lower << endl;
return 0;
}
A const member function, is a member function that does not mutate its member variables.
const on a member function does not imply const char *. Which would mean that you can't change the data in the address the pointer holds.
Your example does not mutate the member variables themselves.
A const on a member function, will ensure that you treat all of your member variables as const.
That means if you have:
int x;
char c;
char *p;
Then you will have:
const int x;
const char c;
char * const p; //<-- means you cannot change what p points to, but you can change the data p points to
There are 2 types of const pointers. A const member function uses the one I've listed above.
A way to get the error you want:
Try changing:
char * rep;
to:
char rep[1024];
And remove this line:
rep = new char [strlen(s)+1];
It will throw the error you are expecting (can't modify members because of const keyword)
Because there is only 1 type of const array. And that means you cannot modify any of its data.
Now the whole system is actually broken with the following example:
class String
{
private:
char rep2[1024];
char* rep;
...
String :: String (const char* s)
{
rep = rep2;
strcpy (rep, s);
}
So the lesson to learn here is that the const keyword on member functions does not ensure that your object will not change at all.
It only ensures that each member variable will be treated as const. And for pointers, there is a big diff between const char * and char * const.
Most of the time a const member function will mean that the member function will not modify the object itself, but this is not always the case, as the above example shows.
The reason is that you don't change rep. If you would, you would find rep = ...; somewhere in your code. This is the difference between
char*const rep;
and
const char* rep;
In your case, the first one is done if you execute a const member-function: The pointer is const. So, you won't be able to reset the pointer. But you will very well be able to change what the pointer points to.
Now, remember rep[i] = ...; is the same as *(rep + i) = ...;. Thus, what you change is not the pointer, but what the pointer points to. You are allowed, since the pointer is not of the second case type.
Solution
The const meaning you are looking at is physical constness. However, a const member-function means your object is logical const. If a change to some content will change the logical constness of your object, for example if it changes some static variable that your object depends upon, your compiler cannot know that your class now has another logical value. And neither it can know that the logical value changes dependent on what a pointer points to: The compiler doesn't try to check logical constness in a const member function, since it cannot know what those member variables mean. This stuff is termed const-correctness.
Use an object that is not just a reference or a pointer: A const member function will make that object const, and will disallow you to change its content. std::string, as proposed by some, or an array of characters (note that an array will disallow you changing its content, as opposed to just a pointer), would be an appropriate choice.
2.
toUpper() does not change the pointer (which belongs to the class). It only changes the data which rep points to (that do not belong to the class).
However, 'const' is a sort of warranty for the users of your class: if a method is declared const, who uses an instance of your class can expect it won't change when calling the method. My point is, if toUpper() changes the state of a string, don't declare it const, whether C++ allows it or not.
The const qualifier means it will not change any members of the class.
In this case rep is the only member of the class and I see no attempt to modify this member. Anything pointed at or referenced outside the class is not considered as part of the class.
A solution to this problem would be to replace the char* with a std::string.
Then you would only be able to call const members of std::string from within toUpper()
For example (use std::string)
class String
{
std::string rep;
void toUpper() const
{
for (int i = 0; rep [i]; i++)
rep[i] = toupper(rep[i]);
// Can only use const member functions on rep.
// So here we use 'char const& std::string::operator[](size_t) const'
// There is a non const version but we are not allowed to use it
// because this method is const.
// So the return type is 'char const&'
// This can be used in the call to toupper()
// But not on the lhs of the assignemnt statement
}
}
You cannot change the value of something declared as
const char* rep;
or
const char* const rep;
Unfortunately, declaring your member const turns into rep into
char* const rep;
which means, you cannot change the acutal address, but you can change the contents, whereas you cannot change the value.
To make const memebrs respect keep your buffer const, you will need to make rep and array of characters or a string object rather than a character pointer.