C++ const semantics for a reference - c++

If there is something like below in code:
func(const base& obj)
what does the const semantics mean? What is constant here ? Is obj a const reference to a non-const object or a non-const reference to a const object?

There is no such thing as a "non-const" reference, that is, a reference is always bound to the same object and there is no way to change that. "const type&" means reference to const type.

obj is a reference to a const object.
There is no such thing as a "non-const reference", since a reference cannot be changed to refer to something else after it has been created.

It's called a const reference. You have a ' referential access ' to data that's passed but you cannot modify it.

Without const you will be not able to send a const object to that function. So it is always a positive to add const. especially when you are creating function for many users. Classic examples are setters functions.
x->setXsth(sth& obj) // works only with non-const object.
x->setXsth(const sth& obj) //works with const object and non-const.

obj is a reference to const base, so it means you are not allowed to change referenced object. It can be written as
func(const base& obj)
or
func(base const & obj)
Use right-left rule for reading such declarations types, for this simple example just read it from the right. More on that here:
http://www.codeproject.com/KB/cpp/complex_declarations.aspx

obj is a constant reference to an object (wether the object is const or non const) passed in argument to func()
if you write : func(B);
this means that you cannot change the content of B inside the function func()
(where func(const base& obj))

Somewhat unsolicited answer/viewpoint: The const modifier modifies whatever is on its left-hand-side, except for the one construction that you're using (in which case it modifies whatever is immediately to the right). I find it easier to always stick const immediately on the right of whatever I want to modify, and read the statement right-to-left. Maybe this isn't the best way to do things, but it helps me to keep it straight.
Examples:
// these two statements are equivalent
const int x = 5; // special case usage
int const x = 5;
// using the LHS syntax makes multiple consts easier to understand
int const y = 6;
int const * const x = &y; // x is a const pointer to const int
// const can apply to pointers but not to references
int const & const z = y; // redundant, references are always const

As other answers have said, obj is a reference to a const base object. However, that doesn't mean the object it refers to either has exactly the base type, or that the object it refers to is const, just that func can't modify obj through that reference. For example:
struct derived : base { ... };
derived d;
func(d);
is legal, and:
bool other_func(const base& b, other_object& o) {
base b_copy = b;
o.foo();
return b_copy == b;
}
may return false if o has an internal non-const reference to b (or something inside it) and o.foo() modifies b. This has practical implications for functions like
std::string::operator=(const std::string& other);
where a naive implementation might do the wrong thing for my_str = my_str.

Related

Why can a const method take a non const reference?

I know a const method cannot modify the object from which it is called. Look at this code:
class A{
int a;
public:
void f(A & a_) const {
a_.a=5;
};
};
int main(){
A x;
x.f(x);
return 0;
}
Why does this code compile? Why can I even assign a reference to a non const object of the same class, when declaring the method as constant? In general how can the compiler check all the possible situations in which the function could modify the object?
I know a const method cannot modify the object from which it is called.
This is an oversimplification, and slightly inaccurate.
A const function merely means that the implicit this pointer is a pointer to const.
Why does this code compile?
Because it is well-formed.
Why can I even assign a reference to a non const object of the same class, when declaring the method as constant?
Because constness of the function does not affect what objects you can modify through a reference.
In general how can the compiler check all the possible situations in which the function could modify the object?
The compiler simply does not make such checks.
A const member function cannot modify the object from which it is called using the "implicit" this parameter. f(...) is (ignoring member visibility) equivalent to the free function
void f(const A* this, A& _a) {
_a.a = 5;
}
If you pass the same object as a non-const pointer or reference, you are still allowed to modify it.

Differentiating between const references to immutable vs. mutable objects

Is there any accepted way in C++ to differentiate between const references to immutable objects vs. mutable ones?
e.g.
class DataBuffer {
// ...
};
class Params {
// ...
};
class C {
public:
// Given references must be valid during instance lifetime.
C(const Params& immutableParameters, const DataBuffer& mutableDataBuffer) :
m_immutableParameters{immutableParameters},
m_mutableDataBuffer{mutableDataBuffer}
{
}
void processBuffer();
private:
const Params& m_immutableParameters;
const DataBuffer& m_mutableDataBuffer;
};
Here the semantic difference is given just in the names.
The problem is that const& instance variables only let you know the object won't be modified by the instance. There is no distinction in the interface whether or not they may be modified elsewhere, which I think is a useful feature to be able to describe in the interface.
Expressing this through the type-system would help make interfaces clearer, allow the compiler to catch errors (e.g. accidentally modifying parameters handed to a C instance, outside of the instance, in the example above), and possibly help with compiler optimizations.
Assuming that the answer is that the distinction isn't possible in C++, maybe there is something close which can be achieved with some templates magic?
Immutability is not part of the C++ type system. As such, you cannot differentiate between immutable objects and mutable ones. And even if you could, std::as_const will always ruin your attempt to do so.
If you are writing an interface that requires immutability of objects, the easiest way to handle this is to invoke the Fundamental Theorem of Software Engineering: "We can solve any problem by introducing an extra level of indirection." So make immutability part of the type system. For example (FYI: uses some small C++17 library stuff):
template<typename T>
class immutable
{
public:
template<typename ...Args>
immutable(std::in_place_t, Args &&...args) t(std::forward<Args>(args)...) {}
immutable() = default;
~immutable() = default;
immutable(const immutable &) = default;
//Not moveable.
immutable(immutable &&) = delete;
//Not assignable.
immutable operator=(const immutable &) = delete;
immutable operator=(immutable &&) = delete;
const T* operator->() const {return &t;}
const T& operator*() const {return t;}
private:
const T t;
};
With this type, the internal T will be immutable regardless of how the user declares their immutable<T>. Your C class should now take an immutable<Params> by const&. And since immutable<T> cannot be constructed from a copy or move of an existing T, the user is forced to use immutable<Params> whenever they want to pass that as a parameter.
Of course, your biggest danger is that they'll pass a temporary. But that was a problem you already needed to solve.
I don't know the reason, but here's how you can do it:
struct C {
template<typename T, typename T2>
C(T&&, const T2&&) = delete;
C(const Params&, const DataBuffer&) { /*...*/ }
};
By declaring a constructor that takes any argument by non-const reference, it will always be a better match than the constructor taking const&, as a cv-qualifier doesn't have to be added.
The const& constructor is a better match when passing a const parameters, as the cv-qualifier doesn't have to be removed.
DataBuffer db;
const Params cp;
C c{ cp, db }; // ok, second constructor call is chosen
Params p;
C c2{ p, db }; // error, constructor is deleted
Due note that, as #IgorTandetnik said, you can break your requirement easily:
Params pa;
const Params& ref_pa = pa;
C c3{ ref_pa, db }; // ok, but shouldn't compile.
As previous answers, C++ doesn't have the concept of "immutable". #Rakete1111 gave you the answer I would have used. However, Visual Studio will put global const variable in .rdata segment, where other variables will go to .data. The .rdata segment will generate a fault when trying to write.
If you need a run time test whether an object is read only, use a signal handler, like this:
#include <csignal>
const int l_ci = 42;
int l_i = 43;
class AV {};
void segv_handler(int signal) {
throw AV{};
}
template <typename T>
bool is_mutable(const T& t)
{
T* pt = const_cast<int*>(&t);
try {
*pt = T();
}
catch (AV av) {
return false;
}
return true;
}
void test_const()
{
auto prev_handler = std::signal(SIGSEGV, segv_handler);
is_mutable(l_i);
is_mutable(l_ci);
}
What you need is not a const reference, but a const object. Value semantics solve your problem. Nobody can modify a const object. While a reference is only const where it is marked const, because the referenced object may not be const. Take that for example :
int a;
int const& b = a;
// b = 4; <-- not compiling, the reference is const
Above, a is int, and b is a reference to const int. While a is not const, the language permit the reference to const to be bound on a non const object. So it's a reference to const object that is bound to a mutable object. The type system won't allow you to modify the mutable object through the reference, because it may have been bound to a const object. In our case it isn't, but the tribe don't change. However, even declaration of a reference to const won't change the original declaration. The int a is still a mutable object. a may still change value:
a = 7;
This is valid, whatever references or other kind of variables have been declared. A variable declared as int (no const) can change, and nothing can prevent it from changing. Heck, even another program like cheat engine can change the value of a mutable variable. Even if you had rules in the language to guarantee that it won't be modified, there is nothing they will prevent the mutable variable from changing values. In any language. In machine language, a mutable value is permitted to change. However, maybe some API of the operating system can help you change the mutability of memory regions.
What can you do to solve this problem now?
If you want to be 100% sure an object won't be modified, you must have immutable data. You usually declare immutable objects with the const keyword :
const int a = 8;
int const& b = a;
// a cannot change, and b is guaranteed to be equal to 8 at this point.
If you don't want a to be immutable and still guarantee b to not change, use values instead of references :
int a = 8;
const int b = a;
a = 9;
// The value of b is still 8, and is guaranteed to not change.
Here, value sematic can help you have what you want.
Then const reference are there for what? There are there to express what you are going to do with the reference, and help enforce what can change where.
As the question has been further clarified, no there is no way to determine if the reference has been bound to a mutable or immutable object in the first place. There is, however, some tricks you can have to differentiate the mutability.
You see, if you want more information about the mutability to be passed along with the instance, you can store that information in the type.
template<typename T, bool mut>
struct maybe_immutable : T {
using T::T;
static constexpr auto mutable = mut;
};
// v--- you must sync them --v
const maybe_immutable<int, false> obj;
This is the most simple way to implement it, but a naive one too. The contained data will be conditionally immutable, but it forces you to sync template parameter and constness. However, the solution allows you to do this :
template<typename T>
void do_something(const T& object) {
if(object.mutable) {
// initially mutable
} else {
// initially const
}
}
I hope I understand you question correct it is not as explicit as so to say "D language" but with const r-value references you can make immutable parameters.
What I understand from immutable is forexample
void foo ( const int&& immutableVar );
foo(4);-> is ok
int a = 5;
foo(a);->is not ok

Is return by value always const?

This code does not compile:
class C {};
void foo (C& c) {}
C bar() { return C(); }
int main()
{
foo(bar());
}
Compilation error (GCC 4.1.2) in line foo(bar()):
invalid initialization of non-const reference of type 'C&'
from a temporary of type 'C'
As bar() returns a mutable object, it should compile...
Why C++ does not allow this above code?
EDIT: I have summarize in an answer below all good ideas from all answers ;-)
The applicable rule here is that you can't create a non-const reference to a temporary object. If foo was declared as foo(const C&) the code would be okay.
The temporary object itself is not const, though; you can call non-const member functions on it, e.g., bar().non_const_member_function().
With C++11, foo can be written to take an rvalue reference; in that case, the call would be okay:
void foo(C&&);
foo(bar()); // okay
It's because the value returned by bar is a temporary value. As it's existence is temporary, you can't use a pointer or reference to that.
However, if you store a copy of that temporary, as in your second change, you no longer pass a reference to a temporary object to foo, but a reference to a real tangible object. And in the first case, when you change to a reference to a constant object, the compiler makes sure the temporary object stays around long enough (as per the C++ specification).
The issue is not with the declaration of bar but with that of foo. foo takes a non-const reference, and temporaries can only bind to const references (which then extends the lifetime of the temporary to match that of the reference it is bound to).
Allowing a non-const reference to bind to a temporary doesn't make much sense. A non-const reference implies that it will modify whatever object is bound to it. Modifying a temporary serves no purpose since its lifetime is limited and the changes will be lost as soon as it goes out of scope.
Modifiable (lvalue-)references do not bind to temporary values. However, const-references do bind to temporary values. It has nothing to do with whether the object returned by value is const or not; it's simply a matter of whether the expression is temporary or not.
For example, the following is valid:
struct C { void i_am_non_const() {} };
int main()
{
bar().i_am_non_const();
}
It is a design choice. There is nothing inherently impossible here. Just a design choice.
In C++11, you have a third alternative which is also superior alternative:
void foo(C && c) {}
That is, use rvalue-references.
It's not const, but it is a temporary rvalue. As such, it can't bind to a non-const lvalue reference.
It can bind to a const or rvalue reference, and you can call member functions (const or not) on it:
class C { void f(); };
void foo_const(C const &);
void foo_rvalue(C &&);
foo_const( bar() ); // OK
foo_rvalue( bar() ); // OK
bar().f(); // OK
The real, hard truth is that it makes no sense to get a reference to a temporary value.
The big point of passing an object by reference is that it allows you to modify its state. However, in the case of a temporary, by its very nature, it would not be particularly helpful to be able to modify it, since you have no way of getting another reference to it later in your code to see the changes.
However, this is somewhat different in the case you have a const reference. Since you'll only ever read from a const reference, it makes total sense to be able to use temporaries there. This is why the compiler will "hack" around it for you, and give a more permanent address to temporaries that you want to "turn" into const references.
So, the rule is that you cannot get a non-const reference to a temporary value. (This slightly changed with C++11, where we have a new type of references that serve this exact purpose, but methods are expected to deal with those in a special way.)
Thank you all for your answers :-)
Here I gather your good ideas ;-)
Answer
Return by value is not const. For example, we can call non-const member functions of return by value:
class C {
public:
int x;
void set (int n) { x = n; } // non-const function
};
C bar() { return C(); }
int main ()
{
bar.set(5); // OK
}
But C++ does not allow non-const references to temporary objects.
However C++11 allow non-const rvalue-references to temporary objects. ;-)
Explanation
class C {};
void foo (C& c) {}
C bar() { return C(); }
//bar() returns a temporary object
//temporary objects cannot be non-const referenced
int main()
{
//foo() wants a mutable reference (i.e. non-const)
foo( bar() ); // => compilation error
}
Three fixes
Change foo declaration
void foo (const C& c) {}
Use another object
int main()
{
C c;
foo( c = bar() );
}
Use C++11 rvalue-reference
void foo(C && c) {}
Moreover
To confirm temporary objects are const, this above source code fails for the same reason:
class C {};
void foo(C& c) {}
int main()
{
foo( C() );
}

Do class member reference variables have in-built "const-correctness"?

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.

Which are the implications of return a value as constant, reference and constant reference in C++?

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.