class A
{
public:
A(int i = 25) {x = i;y=new int[i];for(int j=0;j<i;j++) y[j]=j;}
int& f() const {return x;}
int& operator[](int i) const {return y[i];}
private:
int x,*y;
};
int main()
{
A a(15);
cout << a[5];
cout << a.f();
return 0;
}
When I'm trying to compile the code it says
"Invalid initialization of reference of type int& from expression of type const int"
regarding f() function.
I understand it,since it returns a non-const reference to something declared as const by the function. But shouldn't it behave the same with the [] overloading?
It also returns a non-const reference to something that the function declared as const,but it shows no error there.
What's the difference?
It is telling you that you can't return a non-const lvalue reference to a data member from a const member function. You need
const int& f() const {return x;}
You may decide to provide a non-const overload if needed:
int& f() {return x;}
As for operator[], it does not return a reference to a data member. You cannot modify x or y via operator[], so it is really a const member function. You may decide to disallow modification to the data pointed at by y, and this would make sense if your class models an array. But it isn't strictly necessary and the compiler has no reason to enforce it.
const int& operator[](int i) const {return y[i];}
int& operator[](int i) {return y[i];}
The issue is here:
int& f() const {return x;}
Your function is marked const, so it can be only invoked by const instances. However, you return a non-const reference, therefore if valid you can use it an modify const instances. The compiler is not happy with this. Hence f() should return const int& instead.
On the other hand, int& operator[](int) const compiles, since you return a reference to the data the pointer member y points to, but you cannot modify the pointer itself. In other words, on a const instance, the pointer y is const, i.e. int * const y, but not the data. Therefore bitwise const-ness is preserved, but of course logical const-ness is not, however the compiler only cares about bit-wise const-ness.
To enforce logical const correctness, one option is to write 2 versions of your operator[]:
const int& operator[](int i) const {return y[i];}
and
int& operator[](int i) {return y[i];}
Note that the second version should be marked non-const, as otherwise you'd try to overload two functions that differ only by their return type. If you want to avoid code duplication in the non-const version, you can make use of the const version via a const_cast, like
int& operator[](int i)
{
return const_cast<int&>(const_cast<const A&>(*this)[i]); // use the const version
}
EDIT
There is a proposal to introduce a const-propagating wrapper for pointer-like data members, propagate_const, which in effect will also make the data pointed to const, see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4057.pdf
In C++ language constness of a class implies immediate constness of all its members (except for mutable ones). However, constness of the class does not propagate to data pointed by pointer members or referenced by reference members. That pointed/referenced data is not part of the class and its constness is not affected by constness of the class.
In your example, declaring a member function as const makes it treat members x and y as const. However, the data pointed by y does not become const, e.g. neither *y nor y[i] are const.
Propagation of constness from the enclosing class to the pointed/referenced data is actually a part of user intent. It is something you need or don't need depending on what you are trying to implement. The language leaves it completely up to you (or to a library-level solution).
Related
So all modifiers that I have come across in C++ have came prior to the name of the function. Why is const different? Why must it both precede the name and come prior to the function block?
const int getSize() const;
or
const int getSize const {
...
}
These are 2 different usages of the const keyword.
In this case:
const int getSize() {
the function is returning an int that is const, i.e. the return value cannot be modified. This is not very useful, since the const in the return value is going to be ignored (compilers will warn about this). const in the return type is only useful when returning a const*, or a const&.
In this case:
int getSize() const {
this is a const-qualified member function, i.e. this member function can be called on const objects. Also, this guarantees that the object will not be modified, even if it's non-const.
Of course, you can use both of these together:
const int getSize() const {
which is a const-qualified member function that returns a const int.
The const before the function is applied to the return type of the function. The const after is only for member functions and means that the member function is callable on a const object.
As a note returning a const int does not make sense, many compilers will warn that the const gets discarded.
If I understand correctly, a member function that is not supposed to modify the object should be declared as const to let the users know about the guarantee. Now, what happens when that member function returns the reference to *this? For example:
class C{
public:
C &f() const {return *this;}
};
Inside C::f(), this has the type const C*, so, the following will not compile:
int main() {
C c; // non-constant object!
c.f();
return 0;
}
Of course, we could provide a non-const version of C::f() to work on non-constant objects:
class C{
public:
const C &f() const;
C &f();
};
However, I do not believe that this is what we want. Note that the non-constant version does not change the object, but this promise to the users is not expressed... Am I missing something?
EDIT: Let me just summarize the question for clarity: f() does not modify the object on which it is called, so declaring it as C &f(); without making it a const member is misleading. On the other hand, I do want to be able to call f() on non-const objects. How do I resolve this situation?
EDIT: It comes out from all the discussion that took place in the comments that the question was based on an incorrect understanding of what constness of a function member implies. The correct understanding that I am taking away for myself is:
A member function that returns a non-const reference is intended to allow its users to change the object through the returned reference. Therefore, even though this function does not change the object by itself, there should be no inclination to declare it to be a const member!
Your problem is the original function definition:
C &f() const {return *this;}
here you return a non-const reference to the const object, which would allow changing the const object and would be dangerous, therefore it's forbidden.
If you were to write it as
const C &f() const {return *this;}
would be callable from both const and non-const objects and would always return a const reference.
Of course, we could provide a non-const version of C::f() to work on non-constant objects. However, I do not believe that this is what we want.
Probably that is exactly what you want. It's the only way to return a non-const reference when called on non-const objects and keep const correctness when calling it on const objects.
Consider the following code snippet:
class MyClass {
int x;
public:
MyClass(int val) : x(val) {}
const int& get() const {return x;}
};
void print (const MyClass& arg) {
cout << arg.get() << '\n';
}
int main() {
MyClass foo (10);
print(foo);
return 0;
}
Whether I add a const modifier before the instatiatation of MyClass or not, the program successfully compiles (without any warning) and prints 10. Why can print accept a non-const argument? Or, in other words, what is the function of the const modifier in the function parameter? Why can the formal and actual parameters of a function have different types (or modifiers)?
I have tried both GCC (4.8.2) and Clang (3.4) with -Wall -std=c++11 on Ubuntu 14.04, and the results were the same (no errors/warnings). I have also searched "c++ const object function" but didn't get anything that looked promising.
This is completely sane and normal. The object is treated as const within your function; it does not matter that it was not originally created to be immutable.
Of course, the opposite is not true!
void foo(T& rarr);
int main()
{
const T lolwut;
foo(lolwut); // oops
}
const forbids the body of the function from modifying the parameter variable. Both ways compile because you didn't attempt to modify it.
You can overload const and non-const reference parameters, and the const overload will only be chosen if the argument is really const (or a type conversion results in a temporary being passed). (For non-reference parameters, const seldom makes sense and such overloads may not even be defined.)
All that const does in this case is to prevent modification of parameter variable (and in the case of classes, prevent the calling of functions that are not labelled as const). MyClass may be trivially cast to const MyClass because there should be nothing that you can do to a const MyClass that you can't do to a non-const one. The reverse is not true, of course.
(I say "should" above, because it is of course possible to completely subvert const semantics under C++ if you wanted to, so the presence of const in a function prototype is really only a hopeful hint rather than a cast-iron compiler-enforced guarantee. But no sensible programmer should be breaking things like that!)
Are all of the below declarations the same? If so, what is the standard way to declare a constant function?
const SparseMatrix transpose();
SparseMatrix transpose() const;
const SparseMatrix transpose() const;
The const on the left of the function name means the object that is returned cannot be modified. The const on the right means the method is apart of a class and does not modify any of its data members. Unless or course any of its data members are declared with the mutable keyword, in which case modification thereof is permitted despite a const guard.
The placement of the const keyword is unimportant when the return type of the function is of non-pointer type:
T const f(); // same as const T f();
However, note that the placement of the const keyword matters when using a pointer as the return type. For example:
const T* f();
This method returns a pointer to a const T. That is, what it points to is immutable. So you cannot do an assignment through a dereference of the returned pointer:
T* x = f();
*x = y; // error: assignment of read-only location '*(const T*)x'
When const is placed on the immediate right of the return type (that is a pointer), it means the pointer is const and cannot be changed.
T* const f();
int main()
{
T* x const;
x = f(); // error: assignment of read-only variable 'x'
}
Furthermore, if we have const on both sides of a pointer return type, and have const denoting "no modification of class members", then it's read as the following:
const T* const f() const;
A const member function named f that returns a const pointer to a const T
The first one will return a SparseMatrix that is const and cant be changed.
The second one declares a function that returns a SparseMatrix and assures the function will not change any class variables (assuming it is a member function, otherwise it wouldnt make sense with this deceleration) except for mutable members.
The final one does both.
1) return a const value
2) const function, no member changes inside it
3) 1)+2)
Just curious on why a param has to be a const in operation overloading
CVector& CVector::operator= (const CVector& param)
{
x=param.x;
y=param.y;
return *this;
}
couldn't you have easily done something like this ??
CVector& CVector::operator= (CVector& param) //no const
{
x=param.x;
y=param.y;
return *this;
}
Isn't when something becomes a const, it is unchangeable for the remainder of the applications life ?? How does this differ in operation overloading ???
You don't need const:
#numerical25: Just curious on why a param has to be a const in operation overloading
It's not required, but it is a good design decision.
See the C++ standard Section 12.8-9:
A user-declared copy assignment
operator X::operator= is a non-static
non-template member function of class
X with exactly one parameter of type
X, X&, const X&, volatile X& or const
volatile X&
I think it's a good idea though:
Using a const parameter does seems like a logical design decision to me though because you want to ensure that the other value will not be changed.
It tells other people that use your class that you will not be changing the other value when you say something like: myObject = other; and it enforces this so you can't accidentally change other.
Also if you allowed non const references to the object as the parameter, then you are limiting the amount of objects that can use your function. If it is const it can be used for parameters that are const and non const. If your parameter is non const it can only be used by parameters that are non const.
const only applies to the current reference, not the object:
#numerical25: Isn't when something becomes a const, it is unchangeable for the remainder of the applications life ?? How does this differ in operation overloading ???
A const reference is simply that a reference that is const. It does not change the const-ness of the actual object you are passing in.
An example of non-const operator overloading:
Here is an example of operator overloading where the parameter is not const.
I DO NOT RECOMMEND TO DO THIS THOUGH:
class B
{
public:
const B& operator=(B& other)
{
other.x = 3;
x = other.x;
return *this;
}
int x;
};
void main(int argc, char** argv[])
{
B a;
a.x = 33;
B b;
b.x = 44;
a = b;//both a and b will be changed
return 0;
}
A const parameter is const throughout the function using it, it does not change its constness outside of it.
In this case you want to declare a const argument so that your assignment operator accepts both non-const variables and const variables; the latter case, in particular, includes the result of expressions, which is a temporary const variable and which you generally want to support in assignments.
If you used
CVector& CVector::operator= (CVector& param) // no const
then did this:
const CVector& my_vector = GetMyVector();
some_other_vector = my_vector; // call assignment operator - error!
You'll get an error because my_vector is a const CVector& and that can't be cast to a CVector& (non-const reference). It's just the local reference to it inside the operator= function that is const, not the entire object itself.
You can use the non-const variety, but this has two repercussions, one which is functional, and one which is about what you, as the writer of the function, are telling the user.
1) people calling the function that takes a non-const reference would not be able to call it using a const variable
2) when you have a function argument that's a non-const reference, you're signalling, "I reserver the right to change this". Typically, when a user of your function writes a = b;, he doesn't expect b to change.
Note that there's a third option you could use for this, pass-by-value:
CVector& CVector::operator= (CVector param) //no reference
This doesn't have either of the problems I mention above. However, it's very inefficient. Because of these three factors, passing by reference-to-const is preferred, especially in cases like a vector where copying can be expensive.
For the same reason you would use const anywhere: to ensure that future changes to the method don't inadvertently modify the passed in parameter, to help document the interface to notify callers that it is safe to pass param without risk of it changing, and to allow callers to pass in references that are declared as const in the calling code.
Another reason is to allow for conversions. For example:
string s = "foo";
s = "bar";
Here, an implementation might choose to only provide the assignment operator that takes a const reference to a string as a parameter, and depend on the compiler using a constructor to create a temporary string from the char * "bar". This would not work if the op='s parameter was not const, as you cannot bind a temporary to a non-const reference.
The const qualifier makes the passed parameter (in your example it is 'const CVector& param') as read only. The const qualifier ensures that the parameter (param) is not altered inside the operator=() method.
Without the const qualifier, the following is possible:
CVector& CVector::operator= (CVector& param)
{
x=param.x;
y=param.y;
param.x = 10; // some random value
param.y = 100;
return *this;
}
The above method alters the right hand side operand 'param' after assigning the value to the left hand side operand. The const qualifier helps you not to violate the semantics of the assignment operation.