When reading tutorials and code written in C++, I often stumble over the const keyword.
I see that it is used like the following:
const int x = 5;
I know that this means that x is a constant variable and probably stored in read-only memory.
But what are
void myfunc( const char x );
and
int myfunc( ) const;
?
void myfunc(const char x);
This means that the parameter x is a char whose value cannot be changed inside the function. For example:
void myfunc(const char x)
{
char y = x; // OK
x = y; // failure - x is `const`
}
For the last one:
int myfunc() const;
This is illegal unless it's inside a class declaration - const member functions prevent modification of any class member - const nonmember functions cannot be used. in this case the definition would be something like:
int myclass::myfunc() const
{
// do stuff that leaves members unchanged
}
If you have specific class members that need to be modifiable in const member functions, you can declare them mutable. An example would be a member lock_guard that makes the class's const and non-const member functions threadsafe, but must change during its own internal operation.
The first function example is more-or-less meaningless. More interesting one would be:
void myfunc( const char *x );
This tells the compiler that the contents of *x won't be modified. That is, within myfunc() you can't do something like:
strcpy(x, "foo");
The second example, on a C++ member function, means that the contents of the object won't be changed by the call.
So given:
class {
int x;
void myfunc() const;
}
someobj.myfunc() is not allowed to modify anything like:
x = 3;
This:
void myfunc( const char x );
means you you cannot change x inside the function, i.e. this is illegal:
void myfunc( const char x ) {
x = ...;
}
while:
int myfunc() const;
only makes sense if myfunc() is a method inside a class; it basically means the method cannot modify the class instance (i.e. the state of the instance before and after calling instance.myfunc() will be the same).
Before a variable identifier, const indicates that the variable can be initialized and thereafter not modified.
After a class method name, const indicates that the method will not modify the observable state of the class. The mutable keyword allows internal data to be modified.
Before a pointer or reference variable, const indicates that the identifier will not be used to modify the referenced data, though it may be changed by other means.
const int *pInt = &x;
Const can also be used to indicate that the pointer itself cannot be modified:
int * const pInt = &x;
I've found a very good explanation at: http://duramecho.com/ComputerInformation/WhyHowCppConst.html
Basically all confusion lies in the different use cases for the keyword const. Depending on where you place const you tell that something should be immutable or something should not be able to change something else. 'something' might be a variable or a pointer or a function/method, which you don't want it to be able to change the value of variables passed to the functions or member variables of the object.
void myfunc(const char x) is very similar to const int x = 5 in your example: It declares a constant locally available within the function myfunc. As it is a constant its value cannot be changed.
int myfunc() const is a member function of a class. The const indicates that the function would not change the instance of the class the function is executed on. So, within the function, you cannot do something like this->foo = 7 or call other function that are not const.
The difference between the two is that the first has type void(char) and the second has type int()const.
A function that has such a type with const at the end can only be a member function of a class, and it means that the member function does not change the class value (which this refers to) as seen from outside the class. The compiler will check that to a degree, and any straight write to a class member in a const member function results in a compile time error, and the function can straightly only call const member functions on itself (special directives exist so you can tell the compiler that a member write won't change the class' value as seen from outside. This is done by the mutable keyword).
In the functions you presented, one had a parameter of type char const. Such a parameter cannot be changed inside its function. It has no effect on the function's type though, and no effect to the callers of the function.
The const qualifier means that a variable/pointer defined as const may not be changed by your program and it will receive its value either from an explicit initialization or by a hardware-dependent means.
A pointer that is defined as const in the parameter declarations, the function code will not modify what it points to. Basically, you can use the pointer and it pretty much functions as a "read-only".
For example:-
void foo(const char *x)
{
while(*x)
{
if(*x==' ') cout << '-'; //printing - when a space is encountered
else cout << *x;
x++;
}
}
The above function is fine and won't show any errors. But if foo had any thing that could change the string passed. say a function that replaces spaces with $. Not print $ but changing it to $. Something like this:-
void foo(const char *x)
{
while(*x)
{
if(*x==' ') *x = '$'; //printing - when a space is encountered
else cout << *x;
x++;
}
}
then it would not compile i.e. an assignment error to a read-only memory location.
The accepted answer (and the others I skimmed) are not correct.
Don't assume "const" means an identifier (like your x) "cannot be changed inside the function." It means the identifier can't be changed directly. But it can easily be changed.
Consider the full program (written out for newbies):
#include <iostream>
void break_const(const int x) {
const int* x_ptr = &x;
std::intptr_t z = reinterpret_cast<std::intptr_t>(x_ptr);
int* hacked = reinterpret_cast<int*>(z);
*hacked = 3;
std::cout << "x = " << x << std::endl;
}
int main() {
break_const(5);
return 0;
}
The output is "x = 3."
Edit: I should also add that my statement "It means the identifier can't be changed directly" is a bit off. For ints, it's fine. But for more complicated types, const means even less (e.g., mutable in a class).
Related
Today I found out that code like that works. That sounds really strange to me, because as far as I always knew you can't modify any of members from const member function. You actually can't do it directly, but you can call non-const member function. if you mark member function as const that means that this pointer passed to the function is pointing to const object, then how non-const member function is called in the example bellow?
#include <iostream>
class X
{
public:
void foo() const
{
ptr->bar();
}
void bar() {}
private:
X * ptr;
};
int main()
{
}
Your member variable is not X, but pointer to X. As long as foo does not modify the pointer, it can be const.
When you have a pointer member, then the pointer is const in a const method. You won't be allowed to change the address stored in the pointer. But you can change the pointee all you like.
It's the difference between
X* const cannot_change_pointer; //this is how top-level const applies to pointers
const X* cannot_change_pointee;
What's even more interesting is that const on a method has no effect whatsoever on reference members for the same reason (a const method would only prevent you making a reference refer to something else which can't be done with a reference anyway).
That's seems perfectly OK. The call to foo does not modify the ptr member. Hence, the constness of foo is respected.
my 2 cents
This is related to the (currently) closed question I asked earlier: Can you mutate an object of custom type when it's declared as constant?
Suppose we have something that looks like the following:
class test
{
public:
test() : i{4}, ptr{&i} {};
int i;
int *ptr;
int *get_ptr() const {return ptr;}
};
void func(const test &t, int j) {
*(t.get_ptr()) = j;
// auto ptr = t.get_ptr();
// *ptr = j;
}
int main(int argc, char const *argv[])
{
test t;
std::cout << t.i << std::endl;
func(t, 5);
std::cout << t.i << std::endl;
}
We have this func that takes in a const test &. When I see this signature (and if I didn't look at the implementation of the function), it makes me want to assume that nothing in t will get modified; however, the member variable i is able to be modified through the ptr member variable, as we see here.
I don't usually write code that end up working this way, so I'm wondering if code like this is discouraged?
Furthermore, is it reasonable to assume (most of the time) that an object declared as const will not be mutated?
Yes code like this is definitely discouraged. It completely ignores the reason we have the const keyword in the first place. The function is deliberately modifying something that it is advertising it will not modify
That means that whoever wrote the get_ptr() function messed up because they declared it const but let it return a pointer to non-const object (so one that can be changed, defeating the purpose of declaring the function const)
If whatever get_ptr() returns could properly be modified by such a function then it should be an implementation detail, a private (or protected) variable marked with the mutable keyword.
test::get_ptr() should have two overloads.
const int* get_ptr() const { return ptr; }
int* get_ptr() { return ptr; }
If func() wants to change the test object given to it then it should take test&, not const test&
The key is here:
int *get_ptr() const {return ptr;}
You define get_ptr as const which is akin to saying "get_ptr is not going to change any attributes of the class". And it doesn't. It returns the value of the attribute ptr, which is a int*, and points to a mutable instance of an int which happens to be an attribute of the class as well.
The compiler has no way of knowing this, so from the compiler's perspective the promise was kept. However in reality you're circumventing the const qualifier and allowing to mutate an otherwise immutable attribute.
Not the best of coding practices, but whoever writes such code should, obviously, not expect for i to remain as it was set in the class methods if get_ptr is ever called.
I've been learning C++ and I am practicing with classes at the moment.
I created a class that stores a name and a score of a player and defines functions to
manipulate the data and show it.
One of the functions I created is to compare scores of two players and return a pointer
to the player with the higher score. This is the function:
Player * Player::highestScore(Player p2)const
{
if(p2.pScore>pScore)
{
return &p2;
}
else
{
return this;
}
}
From the main I create the following players:
Player p1("James Gosling",11);
Player *p4 = new Player("Bjarne Stroustrup",5);
I call the highestScore function:
Player *highestScore = p1.highestScore(*p4);
However as you may have noticed from reading the function itself, when I return the pointer to the object that called the method (if it has a higher score), I get an error that says:
return value type does not match the function type
This problem seems to disappear when I declare the return type of the function as a const, like this:
const Player * Player::highestScore(Player p2)const
The part that is confusing me is why does it allow me to return &p2, which is not const and doesn't allow me to return this, which is a pointer to the object that called the function, which isn't a const as well? Also even when I declare the function return type as a const, it still allows me to return &p2, even though the argument passed to the parameter is not a const Player object?
Sorry if the question seems strange or what I'm trying to do is very bad programming, but it's just for the purpose of learning by doing it.
The part that is confusing me is why does it allow me to return &p2, which is not const and doesn't allow me to return this, which is a pointer to the object that called the function, which isn't a const as well?
this is const (or, more accurately, is a pointer-to-const) in a const member function, just like all the data members:
#include <iostream>
#include <type_traits>
struct A
{
void foo()
{
std::cout << std::is_same<decltype(this), const A*>::value << '\n';
}
void bar() const
{
std::cout << std::is_same<decltype(this), const A*>::value << '\n';
}
};
int main()
{
A a;
a.foo();
a.bar();
}
Output:
0
1
Also even when I declare the function return type as a const, it still allows me to return &p2, even though the argument passed to the parameter is not a const Player object?
We can't see what you tried, but presumably it was Player* const, which is not the same as Player const* (or const Player*). You can add constness to &r2 just fine; taking constness away is a different story.
The difference between a const and non-const method is that in the first the this pointer in const and in the latter it is not. So when you try to return non-const pointer from a const function and return this, compiler complains, because there this is const and const-ness can not be automatically removed.
&p2 is simply a pointer to an argument and thus it is not const. Please keep in mind, though that &p2 is pointer to local variable and it is never safe to return that.
When you have a "const" function, you are pretty much promising that "We will not change the object instance in this call". The compiler makes this a const T* this for that type of function (where T is the type of your class, e.g Player).
Obviously, returning a pointer to something that is const as as a non-const pointer is a breach of the rule - because once some code has a non-const pointer to your object, the code can modify the object... Which breaks the promise that "this function won't modify".
So adding const to the return type from function is the right solution here.
You probably also want to change your code so that it takes a const *Player p2 as input - your current code returns a pointer to a local variable [it happens to be an argument, but it's the same principle - it doesn't exist when the function call has returned].
Edit: Unless you are actually returning a copy of something (e.g. an integer, string or a new structure allocated with for example new) in a function with const attribute, the return type should be const.
struct A {
int &r;
A (int &i) : r(i) {}
void foo () const {
r = 5; // <--- ok
}
};
The compiler doesn't generate any error at r = 5;.
Does it mean that &r is already const-correct being a reference (logical equivalent of int* const) ? [Here is one related question.]
I'm not sure exactly what you mean by "already const-correct", but:
Assigning to r is the same as assigning to whatever thing was passed into the constructor of A. You're not modifying anything in the instance of A when you do this, so the fact that foo is declared const isn't an obstacle. It's very much as if you'd done this:
struct A {
int * r;
A (int * i) : r(i) {}
void foo () const { *r = 5; }
}
The fact that foo is const means that it doesn't modify anything in the A instance it's called on. There's no conflict between that and having it modify other data it was supplied with.
Of course if you happened to arrange for r to be a reference to some member of A then calling foo would modify the instance of A after all. The compiler can't catch all possible ways in which constness of a member function might be violated; when you declare a member function const you're promising that it doesn't engage in any such subterfuge.
Yes, it's the logical equivalent of int* const.
You may want to create and use appropriately qualified accessors in this case to prevent unwanted alterations to the value r references.
I interpret a const member function as implicitly inserting const just to the left of every data member that doesn't already have such a qualifier. That const is already there implicitly for references (int & const r; is illegal syntax). In other words, references are "already const-correct" to use your nomenclature.
It would be nice if the const qualifier on a member function had the affect of inserting const in every possible valid position for every data member (e.g., data member int ** foo; acts like int const * const * const foo; in a const member function), but that isn't what happens, and it isn't what the standard says will happen.
class Foo
{
int Bar;
public:
int& GetBar() const
{
return Bar;
}
}
Is it okay that GetBar is a const method? It's not actually changing anything, but it's providing the "outside world" with a means of changing it.
You have a typo in your code, this is what you probably meant:
class Foo
{
int Bar;
public:
int& GetBar() const
{
return Bar; // Removed the ampersand, because a reference is returned, not an address
}
}
And no, this is not legal. When tagging a method with const, not only are you promising you won't touch any of the internal state of the object, you also promise that you will not return anything that can be used to change the state of the object. A non-const reference may be used to modify the value of Bar outside the scope of GetBar(), hence you are implying that you cannot hold the promise.
You must either change the method to being non-const, return a const reference, or make Bar exempt of the promise by marking it as mutable. E.g.: mutable int Bar; The mutable keyword tells the compiler that the logical constness of the object does not depend on the state of Bar. Then you are allowed to do whatever you please with it.
Nope, since you cant do the following assignment:
const int x; int &y = x;
What you can do though is const int x; const int &y = x;
And of course there is no problem in overloading the method and creating both const and non-const variants.
You probably want to return Bar, not &Bar. Anyway, I dropped this code in Comeau:
class Foo
{
int Bar;
public:
int& GetBar() const
{
return &Bar;
}
};
int main(int argc, char** argv)
{
Foo x;
int y = x.GetBar();
y = 5;
return 0;
}
And got the error:
line 9: error: qualifiers dropped in binding reference of type
"int &" to initializer of type "const int"
return Bar;
^
First of all, to return a reference you don't need to use the ampersand operator on the referenced object; it is needed if you want to get a pointer to it. So, I assume you wanted to write return Bar;.
Then, no, you can't do that; in a const method you have a const this pointer (in your case it would be a const Foo *), which means that any reference you can get to its fields1 will be a const reference, since you're accessing them through a "const path".
Thus, if you try to do what you did in that code you'll get a compilation error, since you'd be trying to initialize an int & (the return value of your method) with a const int & (the reference you get from Bar), which obviously is forbidden.
g++ actually says:
testconstref.cpp: In member function ‘int& Foo::GetBar() const’:
testconstref.cpp:9: error: invalid initialization of non-const reference of type ‘int&’ from a temporary of type ‘const int*’
which is what I just said. :)
If, instead, you return a const reference to a class field from a const method you'll have no problem.
Excluding fields marked as mutable, which tells to the compiler that such fields are modifiable even from const methods; this exception has been introduced to allow const methods to change the "real" state of the object in cases when this do not alter its "logical" state; this can be useful to implement lazy evaluation, reference counting, ...
The members of your class are considered const when you are in a const method. So although Bar is int within your class and not const int, in the context of GetBar() const it is "const int". Therefore returning it as a non-const reference or pointer is just as illegal as doing:
const int y = 57;
int& z = y;
This breaks const-correctness even though the 2nd line has not actually changed anything (yet) in y.
Note incidentally that if your class has pointer members the only thing that is const is the pointers themselves and not what they point to. (Often referred to as "shallow" constness)
Thus this would be legal:
class A
{
Foo * foo;
public:
Foo * getFoo() const // legal. does not have to return const Foo *
{
return foo;
}
};
Note that in your original code you would be allowed to return Bar by non-const reference if it were mutable, because those members are not bound by the constness of member functions.
const modifier to a member function doesn't allow to change the object's state with in it's scope. Compiler just checks whether this function is modifying the state of the object or not in its scope. Taking another example -
class foo
{
int num ;
public :
foo( int anum )
{
anum = 10;
}
int getNum()
{
return num;
}
};
foo obj;
int& myNum = obj.getNum() ; // myNum is just an alias to the private class variable num
myNum = 40; // Actually changes the content of the private variable.
So, compiler just checks access specifiers ( i.e., whether this variable is accessible or not in this scope) but not about the private/public/protected variable's memory location if returned to some other variable.