Related
Recently I have been learning about good programming practice in C++ and found out that many programs pass objects to functions by reference so that multiple instances are not created. I have also learned that passing a constant reference prevents the original object from being modified however I do not understand how this works exactly. Shouldn't a constant reference create a new instance because the original object cannot be modified through the reference but the reference can still be used like a separate object? I'm fairly certain that this is not how it works but then, how does it work? Is there something I missed?
I have also learned that passing a constant reference prevents the original object from being modified [...]
Not quite. You are not allowed to modify the object through the const &. In other words, you have read-only access. But nothing natively prevents other code with read-write access (for example the original owner of the referred object) to modify it. You do need to be careful when designing so that such changes do not surprise you.
A constant reference (const&) is similar to a pointer to a constant object. You are allowed to read it through the reference but not modify it. Others, holding a non-const reference can still modify it.
Shouldn't a constant reference create a new instance because the
original object cannot be modified through the reference but the
reference can still be used like a separate object?
It's better to call it a reference to a constant object. This makes it much clearer how the thing works. Calling it the other way around is just confusing because any reference is constant (meaning you can't let it refer to another object after initialization).
So a reference to a constant object is just an additional name for an existing object (like a non-const reference) with the restriction that this name only allows reading from the existing object.
This means that through a reference to a constant object you can:
only read from member variables of the object, but not assign to them, unless a member is marked as mutable
only call methods of the object that are marked as const
Example:
struct Foo
{
int a;
mutable int b;
void SetA( int newA ) { a = newA; }
int GetA() const { return a; }
};
void DoSomething( const Foo& f )
{
// Here, f is just another name for foo, but it imposes some restrictions:
f.a = 42; // compiler error, can't modify member!
f.SetA( 42 ); // compiler error, can't call non-const method!
int x = f.a; // OK, reading is allowed.
f.b = 42; // OK, because b is marked as mutable
int y = f.GetA(); // OK, because GetA() is marked as const
}
int main()
{
Foo foo;
DoSomething( foo );
}
I understand that having a const method in C++ means that an object is read-only through that method, but that it may still change otherwise.
However, this code apparently changes an object through a const reference (i.e. through a const method).
Is this code legal in C++?
If so: Is it breaking the const-ness of the type system? Why/why not?
If not: Why not?
Note 1: I have edited the example a bit, so answers might be referring to older examples.
Edit 2: Apparently you don't even need C++11, so I removed that dependency.
#include <iostream>
using namespace std;
struct DoBadThings { int *p; void oops() const { ++*p; } };
struct BreakConst
{
int n;
DoBadThings bad;
BreakConst() { n = 0; bad.p = &n; }
void oops() const { bad.oops(); } // can't change itself... or can it?
};
int main()
{
const BreakConst bc;
cout << bc.n << endl; // 0
bc.oops(); // O:)
cout << bc.n << endl; // 1
return 0;
}
Update:
I have migrated the lambda to the constructor's initialization list, since doing so allows me to subsequently say const BreakConst bc;, which -- because bc itself is now const (instead of merely the pointer) -- would seem to imply (by Stroustrup) that modifying bc in any way after construction should result in undefined behavior, even though the constructor and the caller would have no way of knowing this without seeing each others' definitions.
The oops() method isn't allowed to change the constness of the object. Furthermore it doesn't do it. Its your anonymous function that does it. This anonymous function isn't in the context of the object, but in the context of the main() method which is allowed to modify the object.
Your anonymous function doesn't change the this pointer of oops() (which is defined as const and therefore can't be changed) and also in no way derives some non-const variable from this this-pointer. Itself doesn't have any this-pointer. It just ignores the this-pointer and changes the bc variable of the main context (which is kind of passed as parameter to your closure). This variable is not const and therefore can be changed. You could also pass any anonymous function changing a completely unrelated object. This function doesn't know, that its changing the object that stores it.
If you would declare it as
const BreakConst bc = ...
then the main function also would handle it as const object and couldn't change it.
Edit:
In other words: The const attribute is bound to the concrete l-value (reference) accessing the object. It's not bound to the object itself.
You code is correct, because you don't use the const reference to modify the object. The lambda function uses completely different reference, which just happen to be pointing to the same object.
In the general, such cases does not subvert the type system, because the type system in C++ does not formally guarantee, that you can't modify the const object or the const reference. However modification of the const object is the undefined behaviour.
From [7.1.6.1] The cv-qualifiers:
A pointer or reference to a cv-qualified type need not actually point
or refer to a cv-qualified object, but it is treated as if it does; a
const-qualified access path cannot be used to modify an object even if
the object referenced is a non-const object and can be modified through
some other access path.
Except that any class member declared mutable (7.1.1) can be modified,
any attempt to modify a const object during its lifetime (3.8) results
in undefined behavior.
I already saw something similar. Basically you invoke a cost function that invoke something else that modifies the object without knowing it.
Consider this as well:
#include <iostream>
using namespace std;
class B;
class A
{
friend class B;
B* pb;
int val;
public:
A(B& b);
void callinc() const;
friend ostream& operator<<(ostream& s, const A& a)
{ return s << "A value is " << a.val; }
};
class B
{
friend class A;
A* pa;
public:
void incval() const { ++pa->val; }
};
inline A::A(B& b) :pb(&b), val() { pb->pa = this; }
inline void A::callinc() const { pb->incval(); }
int main()
{
B b;
const A a(b); // EDIT: WAS `A a(b)`
cout << a << endl;
a.callinc();
cout << a << endl;
}
This is not C++11, but does the same:
The point is that const is not transitive.
callinc() doesn't change itself a and incval doesn't change b.
Note that in main you can even declare const A a(b); instead of A a(b); and everything compile the same.
This works from decades, and in your sample you're just doing the same: simply you replaced class B with a lambda.
EDIT
Changed the main() to reflect the comment.
The issue is one of logical const versus bitwise const. The compiler
doesn't know anything about the logical meaning of your program, and
only enforces bitwise const. It's up to you to implement logical const.
This means that in cases like you show, if the pointed to memory is
logically part of the object, you should refrain from modifying it in a
const function, even if the compiler will let you (since it isn't part
of the bitwise image of the object). This may also mean that if part of
the bitwise image of the object isn't part of the logical value of the
object (e.g. an embedded reference count, or cached values), you make it
mutable, or even cast away const, in cases where you modify it without
modifying the logical value of the object.
The const feature merely helps against accidental misuse. It is not designed to prevent dedicated software hacking. It is the same as private and protected membership, someone could always take the address of the object and increment along the memory to access class internals, there is no way to stop it.
So, yes you can get around const. If nothing else you can simply change the object at the memory level but this does not mean const is broken.
Have a look at the following code. The goal here is to return a reference through two functions (from ReferenceProvider::getReference() to getRef() to main()):
#include <tchar.h>
#include <assert.h>
#include <string>
class BaseClass {
public:
virtual void write() const {
printf("In base class\n");
}
};
typedef BaseClass* BaseClassPointer;
class ChildClass : public BaseClass {
public:
virtual void write() const {
printf("In child class\n");
}
};
typedef ChildClass* ChildClassPointer;
//////////////////////////////////////////////////////////////////////////
ChildClass* g_somePointer = new ChildClass();
class ReferenceProvider {
public:
const BaseClassPointer& getReference() {
const BaseClassPointer& val = g_somePointer;
return val;
}
};
ReferenceProvider g_provider;
const BaseClassPointer& getRef() {
std::string test;
const BaseClassPointer& val = g_provider.getReference();
return val;
}
int _tmain(int argc, _TCHAR* argv[]) {
BaseClass* child = getRef();
assert(child == g_somePointer);
child->write();
return 0;
}
Now, when debugging this code (in Visual C++), breaking at return val; in getRef() will give you a screen like this:
Notice how the values of g_somePointer and val are the same. Now, step over the return statement and you'll get a screen like this:
Notice how val has become invalid (0xcccccccc). This is probably because the stack of getRef() has been cleared and val is no longer available.
The problem now is that child in _tmain() will get this invalid value (0xcccccccc) rendering child unusable. So my first (and main) question is: How to do this correctly?
(Please note that this is just an boiled down example from some other code I've been working on. It needs to be structured like with, including using references to pointers.)
What's making this whole thing very strange (and hard to debug) is that the function getRef() works under some conditions:
If you change the type of g_somePointer to BaseClass* (from ChildClass*)
If you remove the local variable in getRef() (i.e. the line std::string test;)
In both cases the reference variable val (in getRef()) will not become invalid and the function will return the correct pointer address. Can anybody explain this to me?
The problem is here:
const BaseClassPointer& val = g_somePointer;
Since g_somePointer has a different type (ChildClass* is convertible to BaseClass*, but is not the same type), val cannot refer directly to g_somePointer. Instead, a temporary copy is made, converted to the correct type, and val refers to that.
The temporary only lasts as long as val, going out of scope at the end of the function, so the function returns an invalid reference.
If you change the type of g_somePointer to BaseClass* (from ChildClass*)
In that case, no pointer conversion is required, and so val can refer directly to g_somePointer. The code is then correct, but fragile.
If you remove the local variable in getRef() (i.e. the line std::string test;)
With the string variable, there is a destructor call at the end of the function, which overwrites the defunct stack frame that contains the temporary pointer. Without it, nothing overwrites the memory, so the code appears to work - which is unfortunate, as it makes the error much harder to notice.
You can never return a reference to a local object: it will always go out of scope when the function is exited. Sometimes it may appear as if it works but this is just because the data is normally not change when the stack pointer is adjusted.
To explain what's going on:
const BaseClassPointer& val = g_somePointer;
This line is the problem. Let's do away with the typedef:
BaseClass* const& val = g_somePointer;
Here, the type of g_somePointer is ChildClass*. In order to assign it to a BaseClass*, a conversion is needed. From that conversion, a temporary pointer is introduced. That pointer is bound to a reference-to-const, which extends the temporaries lifetime until the reference dies, which is exactly the case after your return val; statement. At that point, the temporary base-class pointer doesn't exist anymore and you have undefined behaviour.
To avoid all that mess, just return a BaseClass*.
You don't want to do something like that. Returning a reference to local memory is the same as returning the address of local memory, which is undefined behavior. All sorts of things can go wrong (or by chance, things can go right).
If you want the "val" to "survive", getReference() method should return a reference to a static object. Is that "static" going to work in your current architecture is another question.
You're returning a const reference to the local val instead of the returned getRef().
Also, how do you transform the pointer to a reference?
const BaseClassPointer& val = g_somePointer;
return val;
won't work if g_somePointer is a pointer - did you use *g_somePointer or similar?
In C++ a stack-allocated object can be declared const:
const Class object;
after that trying to call a non-const method on such object is undefined behaviour:
const_cast<Class*>( &object )->NonConstMethod(); //UB
Can a heap-allocated object be const with the same consequences? I mean is it possible that the following:
const Class* object = new Class();
const_cast<Class*>( object )->NonConstMethod(); // can this be UB?
is also undefined behaviour?
Yes. It's legal to construct and destroy a const heap object. As with other const objects, the results of manipulating it as a non-const object (e.g. through a const_cast of a pointer or reference) causes undefined behaviour.
struct C
{
C();
~C();
};
int main()
{
const C* const p = new const C;
C* const q = const_cast<C*>(p); // OK, but writes through q cause UB
// ...
delete p; // valid, it doesn't matter that p and *p are const
return 0;
}
In your heap example, new returns a pointer to non-const. The fact that you've stored it in a pointer to const (and then const_casted it back to a pointer to non-const) doesn't change the fact that the object itself is not const in the same way as the stack-allocated one is.
However, you can create a const object on the heap:
const Class* object = new const Class();
In such a case, casting to a pointer to non-const and calling a non-const method would be the same situation as the const stack-allocated object.
(The idea of creating a const object on the heap was new to me, I had never seen that before. Thanks to Charles Bailey.)
Yes, a heap-allocated object can be const. Consider this excerpt from the example in 7.1.5.1/5:
const int* ciq = new const int (3); // initialized as required
int* iq = const_cast<int*>(ciq); // cast required
*iq = 4; // undefined: modifies a const object
The example you gave in the question is fine because you're not asking new to make a const object; you're just storing the result in a pointer-to-const.
Don't forget mutable members
It won't be undefinied behaviour if the NonConstMethod only modifies mutable qualified members (see 7.1.5.1 (4)) of a const qualified class. Yes, otherwise it's undefined behaviour.
const A* p = new(const A);
A *q = const_cast<A*>(p);
q->NonConstMethodThatModifiesMembers(); // undefined behaviour!
q->NonConstMethodThatOnlyModifiesMutableMembers(); // defined behaviour!
Obviously:
struct Foo {
const int Bar;
Foo() : Bar(42) { }
};
Foo* foo = new Foo;
const_cast<int&>(foo->Bar); // don't do this.
const_cast can cause UB when the object is actually read-only (for example, the compiler can create such objects when you use hard coded strings in your code, by placing them in certain memory areas that are read only) for some reason. This will not happen with heap allocated objects, no matter how you keep their reference (const pointer, const reference, whatever).
I'm learning C++ and I'm still confused about this. What are the implications of return a value as constant, reference and constant reference in C++ ? For example:
const int exampleOne();
int& exampleTwo();
const int& exampleThree();
Here's the lowdown on all your cases:
• Return by reference: The function call can be used as the left hand side of an assignment. e.g. using operator overloading, if you have operator[] overloaded, you can say something like
a[i] = 7;
(when returning by reference you need to ensure that the object you return is available after the return: you should not return a reference to a local or a temporary)
• Return as constant value: Prevents the function from being used on the left side of an assignment expression. Consider the overloaded operator+. One could write something like:
a + b = c; // This isn't right
Having the return type of operator+ as "const SomeType" allows the return by value and at the same time prevents the expression from being used on the left side of an assignment.
Return as constant value also allows one to prevent typos like these:
if (someFunction() = 2)
when you meant
if (someFunction() == 2)
If someFunction() is declared as
const int someFunction()
then the if() typo above would be caught by the compiler.
• Return as constant reference: This function call cannot appear on the left hand side of an assignment, and you want to avoid making a copy (returning by value). E.g. let's say we have a class Student and we'd like to provide an accessor id() to get the ID of the student:
class Student
{
std::string id_;
public:
const std::string& id() const;
};
const std::string& Student::id()
{
return id_;
}
Consider the id() accessor. This should be declared const to guarantee that the id() member function will not modify the state of the object. Now, consider the return type. If the return type were string& then one could write something like:
Student s;
s.id() = "newId";
which isn't what we want.
We could have returned by value, but in this case returning by reference is more efficient. Making the return type a const string& additionally prevents the id from being modified.
The basic thing to understand is that returning by value will create a new copy of your object. Returning by reference will return a reference to an existing object. NOTE: Just like pointers, you CAN have dangling references. So, don't create an object in a function and return a reference to the object -- it will be destroyed when the function returns, and it will return a dangling reference.
Return by value:
When you have POD (Plain Old Data)
When you want to return a copy of an object
Return by reference:
When you have a performance reason to avoid a copy of the object you are returning, and you understand the lifetime of the object
When you must return a particular instance of an object, and you understand the lifetime of the object
Const / Constant references help you enforce the contracts of your code, and help your users' compilers find usage errors. They do not affect performance.
Returning a constant value isn't a very common idiom, since you're returning a new thing anyway that only the caller can have, so it's not common to have a case where they can't modify it. In your example, you don't know what they're going to do with it, so why should you stop them from modifying it?
Note that in C++ if you don't say that something is a reference or pointer, it's a value so you'll create a new copy of it rather than modifying the original object. This might not be totally obvious if you're coming from other languages that use references by default.
Returning a reference or const reference means that it's actually another object elsewhere, so any modifications to it will affect that other object. A common idiom there might be exposing a private member of a class.
const means that whatever it is can't be modified, so if you return a const reference you can't call any non-const methods on it or modify any data members.
Return by reference.
You can return a reference to some value, such as a class member. That way, you don't create copies. However, you shouldn't return references to values in a stack, as that results in undefined behaviour.
#include <iostream>
using namespace std;
class A{
private: int a;
public:
A(int num):a(num){}
//a to the power of 4.
int& operate(){
this->a*=this->a;
this->a*=this->a;
return this->a;
}
//return constant copy of a.
const int constA(){return this->a;}
//return copy of a.
int getA(){return this->a;}
};
int main(){
A obj(3);
cout <<"a "<<obj.getA()<<endl;
int& b=obj.operate(); //obj.operate() returns a reference!
cout<<"a^4 "<<obj.getA()<<endl;
b++;
cout<<"modified by b: "<<obj.getA()<<endl;
return 0;
}
b and obj.a "point" to the same value, so modifying b modifies the value of obj.a.
$./a.out
a 3
a^4 81
modified by b: 82
Return a const value.
On the other hand, returning a const value indicates that said value cannot be modified. It should be remarked that the returned value is a copy.:
For example,
constA()++;
would result in a compilation error, since the copy returned by constA() is constant. But this is just a copy, it doesn't imply that A::a is constant.
Return a const reference.
This is similiar to returning a const value, except that no copy is return, but a reference to the actual member. However, it cant be modified.
const int& refA(){return this->a;}
const int& b = obj.refA();
b++;
will result in a compilation error.
const int exampleOne();
Returns a const copy of some int. That is, you create a new int which may not be modified. This isn't really useful in most cases because you're creating a copy anyway, so you typically don't care if it gets modified. So why not just return a regular int?
It may make a difference for more complex types, where modifying them may have undesirable sideeffects though. (Conceptually, let's say a function returns an object representing a file handle. If that handle is const, the file is read-only, otherwise it can be modified. Then in some cases it makes sense for a function to return a const value. But in general, returning a const value is uncommon.
int& exampleTwo();
This one returns a reference to an int. This does not affect the lifetime of that value though, so this can lead to undefined behavior in a case such as this:
int& exampleTwo() {
int x = 42;
return x;
}
we're returning a reference to a value that no longer exists. The compiler may warn you about this, but it'll probably compile anyway. But it's meaningless and will cause funky crashes sooner or later. This is used often in other cases though. If the function had been a class member, it could return a reference to a member variable, whose lifetime would last until the object goes out of scope, which means function return value is still valid when the function returns.
const int& exampleThree();
Is mostly the same as above, returning a reference to some value without taking ownership of it or affecting its lifetime. The main difference is that now you're returning a reference to a const (immutable) object. Unlike the first case, this is more often useful, since we're no longer dealing with a copy that no one else knows about, and so modifications may be visible to other parts of the code. (you may have an object that's non-const where it's defined, and a function that allows other parts of the code to get access to it as const, by returning a const reference to it.
Your first case:
const int exampleOne();
With simple types like int, this is almost never what you want, because the const is pointless. Return by value implies a copy, and you can assign to a non-const object freely:
int a = exampleOne(); // perfectly valid.
When I see this, it's usually because whoever wrote the code was trying to be const-correct, which is laudable, but didn't quite understand the implications of what they were writing. However, there are cases with overloaded operators and custom types where it can make a difference.
Some compilers (newer GCCs, Metrowerks, etc) warn on behavior like this with simple types, so it should be avoided.
I think that your question is actually two questions:
What are the implications of returning a const.
What are the implications of returning a reference.
To give you a better answer, I will explain a little more about both concepts.
Regarding the const keyword
The const keyword means that the object cannot be modified through that variable, for instance:
MyObject *o1 = new MyObject;
const MyObject *o2 = o1;
o1->set(...); // Will work and will change the instance variables.
o2->set(...); // Won't compile.
Now, the const keyword can be used in three different contexts:
Assuring the caller of a method that you won't modify the object
For example:
void func(const MyObject &o);
void func(const MyObject *o);
In both cases, any modification made to the object will remain outside the function scope, that's why using the keyword const I assure the caller that I won't be modifying it's instance variables.
Assuring the compiler that a specific method do not mutate the object
If you have a class and some methods that "gets" or "obtains" information from the instance variables without modifying them, then I should be able to use them even if the const keyword is used. For example:
class MyObject
{
...
public:
void setValue(int);
int getValue() const; // The const at the end is the key
};
void funct(const MyObject &o)
{
int val = o.getValue(); // Will compile.
a.setValue(val); // Won't compile.
}
Finally, (your case) returning a const value
This means that the returned object cannot be modified or mutated directly. For example:
const MyObject func();
void func2()
{
int val = func()->getValue(); // Will compile.
func()->setValue(val); // Won't compile.
MyObject o1 = func(); // Won't compile.
MyObject o2 = const_cast<MyObject>(func()); // Will compile.
}
More information about the const keyword: C++ Faq Lite - Const Correctness
Regarding references
Returning or receiving a reference means that the object will not be duplicated. This means that any change made to the value itself will be reflected outside the function scope. For example:
void swap(int &x, int &y)
{
int z = x;
x = y;
y = z;
}
int a = 2; b = 3;
swap(a, b); // a IS THE SAME AS x inside the swap function
So, returning a reference value means that the value can be changed, for instance:
class Foo
{
public:
...
int &val() { return m_val; }
private:
int m_val;
};
Foo f;
f.val() = 4; // Will change m_val.
More information about references: C++ Faq Lite - Reference and value semantics
Now, answering your questions
const int exampleOne();
Means the object returned cannot change through the variable. It's more useful when returning objects.
int& exampleTwo();
Means the object returned is the same as the one inside the function and any change made to that object will be reflected inside the function.
const int& exampleThree();
Means the object returned is the same as the one inside the function and cannot be modified through that variable.
Never thought, that we can return a const value by reference and I don't see the value in doing so..
But, it makes sense if you try to pass a value to a function like this
void func(const int& a);
This has the advantage of telling the compiler to not make a copy of the variable a in memory (which is done when you pass an argument by value and not by reference). The const is here in order to avoid the variable a to be modified.