This is a code for adding 3 objects of class Integer
#include <iostream>
using namespace std;
class Integer
{
int value;
public:
Integer(int i)
{
value = i;
};
int getValue()
{
return value;
}
friend Integer operator+(const Integer & a,const Integer & b)
{
Integer i(a.value+b.value);
return i;
}
};
int main()
{
Integer a(1), b(2), c(3);
Integer d = a + b + c; //****HERE****
cout << d.getValue() << endl;
return 0;
}
Can someone please explain why const is added to parameters in this case. If I remove const, during compilation I get invalid operands to binary operation error.
Adding to this question:
When I only add a and b without const, output is 3 without any errors. However, adding 3 a+b+c objects, I get an error. Why is that?
Let's say hypothetically we had written this function:
friend Integer operator+(Integer& a, Integer& b);
Stylistically, this isn't great because operator+ shouldn't be modifying its arguments. But let's say we don't care about that. Now, what happens when we try to do this:
Integer d = a + b + c;
This is grouped as (a+b)+c. The a+b subexpression will find our operator+(), which is a viable function because a and b are non-const lvalues of type Integer, and the result of that expression is an rvalue of type Integer.
The subsequent expression is {some rvalue Integer} + c. That expression we won't be able to find a viable function for. While c can bind to Integer&, the first argument cannot - you can't bind an rvalue to a non-const lvalue reference. So you'd have to write something like:
Integer tmp = a + b; // OK
Integer d = tmp + c; // also OK
but now we're introducing this extra name into our scope and an extra statement into our code just to get around the unviability of our function signature.
Because you're trying to bind a Temporary to a [non-const-]reference, which isn't allowed in C++.
In the code Integer d = a + b + c;, a + b is evaluated first, and creates a new temporary object which then gets evaluated against c. So the code then becomes Integer d = t<a+b> + c;. Because the first object is now a temporary, only three function signatures will accept it as a parameter: const Integer &, Integer, and Integer &&. The second is not advised because it will create a copy of the object (though, sometimes that's what you want!), and the latter isn't advised because it will refuse to accept persistent objects. The first option, then, is best, because it will accept any Integer object regardless of whether it's expiring or not.
Recommended Reading: What are rvalues, lvalues, xvalues, glvalues, and prvalues? It's very verbose, and will probably require several passes before you "get" it, but it's extremely valuable knowledge for any C++ programmer.
Related
I've been trying to learn how to use a singleton design pattern and stumbled across this:
static S& getInstance()
{
static S instance;
return instance;
}
I noticed here that it returns instance, shouldn't it be supposed to return an instance&?
Edit: I should also mention that the compiler doesn't seem to be making any complaints.
return instance; does return a reference to instance. That is just like
int& r = a;
for some int a creates a reference to a without needing to write a& or something like this.
No, it's correct as written. The compiler will create and return a reference to instance from the function getInstance.
You ask whether it should be instance&. First of all, that is not syntactically valid. What about &instance then? That would take the address of instance and would, therefore, return an instance *.
The multiple uses of the & operator are a common source of confusion. Here's a quick "cheat-sheet" that doesn't use complex terminology from the standard:
The binary (two argument) version of & is applied against two instances of variables and represents bitwise AND (except when overloaded).
unsigned int a = 7;
unsigned int b = 3;
unsigned int c = a & b; // c = 3
The unary (one argument) version of & when applied to a type denotes a reference to a type. Example:
int a = 7;
int& ref_a = a; // ref_a is a reference to a
The unary (one argument) version of & when applied to a variable denotes the address-of operator and yields a pointer to the variable.
int a = 7;
int* ptr_a = &a;
I was going through reference return and came across temporary objects. I don't understand how to identify them. Please explain using this example:
If a and b are objects of same class, consider binary operator+. If you use it in an expression such as f(a+b), then a+b becomes temporary object and f has to of form f(const <class name>&) or f(<class name>). It can't be of form f(<class name>&) However, (a+b).g() is perfectly alright where g() can even change contents of object returned by a+b.
When you say f(a + b), the parameter of f needs to bind to the value with which the function was called, and since that value is an rvalue (being the value of a function call with non-reference return type)*, the parameter type must be a const-lvalue-reference, an rvalue-reference or a non-reference.
By constrast, when you say (a + b).g(), the temporary object is used as the implicit instance argument in the member function call, which does not care about the value category. Mutable values bind to non-const and const member functions, and const values only bind to const member functions (and similarly for volatile).
Actually, C++11 did add a way to qualify the value category of the implicit instance argument, like so:
struct Foo()
{
Foo operator+(Foo const & lhs, Foo const & rhs);
void g() &; // #1, instance must be an lvalue
void g() &&; // #2, instance must be an rvalue
}
Foo a, b;
a.g(); // calls #1
b.g(); // calls #1
(a + b).g(); // calls #2
*) this is the case for an overloaded operator as in this example, and also for built-in binary operators. You can of course make overloaded operators which produce lvalues, though going against the common conventions would probably be considered very confusing.
Your confusion comes not from that you cannot identify temporary objects, in both cases result of a+b is temporary object, but wrong assumption that non const method requires lvalue and would not accept temporary object, which is not true.
For a simple case, think of following piece of code:
int func(int lhs, int rhs)
{
return lhs + rhs;
}
int main() {
int a = 1, b = 2, c = 3;
return func(a * c, b * c);
}
Because func takes two integers, the program must calculate the values of a * c and b * c and store them somewhere -- it can't store them in a or b or c. So the resulting code is equivalent to:
int lhsParam = a * c;
int rhsParam = b * c;
return func(lhsParam, rhsParam);
Again, at the end of func() we return a calculate value, lhs + rhs. The compiler must store it in a new place.
For integers and so forth this seems very simple, but consider instead
int function(std::string filename);
function("hello");
filename has to be a std::string, but you passed a const char*. So what the compiler does is:
std::string filenameParam = "hello"; // construct a new object
function(filenameParam);
just like the previous example, but this time it is hopefully clearer that we're constructing a temporary object.
Note: The convention of calling them "somethingParam" is just for clarity in this answer.
Hello i'm trying to figure out this thing..
Say i have this code.
int a = 5;
double& b = a; //Error.
Then once I've declared the second line as a const the compiler doesn't complain anymore.
const double& b = a; //Correct.
what is really going on behind the scene, why const solves the problem.
An int needs to be converted to double first. That conversion yields a prvalue temporary and these can't bind to references to non-const.
A reference to const will extend the lifetime of the temporary that would otherwise be destroyed at the end of the expression it was created in.
{
int a = 0;
float f = a; // a temporary float is created here, its value is copied to
// f and then it dies
const double& d = a; // a temporary double is created here and is bound to
// the const-ref
} // and here it dies together with d
If you're wondering what a prvalue is, here's a nice SO thread about value categories.
What's happening behind the scenes is an implicit conversion of
an int to a double. The result of an implicit conversion is not
an lvalue, so it cannot be used to initialize a non-const
reference.
This is a follow up question from Calling constructor in return statement.
This a operator overload fun in a class.
const Integer operator+(const Integer& IntObject)
{
cout << "Data : " << this->data << endl;
return Integer(this->data + IntObject.data);
}
What is the relevance of const in the return type for such functions?
int main()
{
Integer A(1); //Create 2 object of class Integer
Integer B(2);
const Integer C = A + B; //This will work
Integer D = A + B; //This will also work
fun(A + B); //Will work
}
void fun(Integer F) {}
This is a case temporaries are not created during return step due to NRVO. The object to be returned is directly constructed on the callee's address.
Here's a better example:
struct Foo
{
void gizmo();
Foo const operator+(Foo const & rhs);
};
Now if you have a Foo x; Foo y;, then you cannot say:
(x + y).gizmo(); // error!
The constant return value means you cannot use it for non-constant operations. For primitive types this is not quite so relevant, because there aren't many non-constant operations you can perform on temporary objects, because lots of "interesting" operations (like prefix-++) aren't allowed on temporaries.
That said, with C++11 one should really try and adopt the new idiom of never returning constant values, since non-constant values are now amenable to move optimisations.
Some people used to suggest doing that, to prevent writing nonsense like A + B = C. However, in C++11 it can prevent some optimisations since it makes the return value unmovable. Therefore, you shouldn't do it.
In this case, it also prevents you from writing perfectly valid code like D = A + B + C, but that's just because the author forgot to declare the operator const.
There is no relevance in your code snippet, because you are making a copy of the returned value.
In general, it is difficult to find good reasons to return a const value. I can only see it having an effect in this type of expression, attempting to call a non-const method on a const temporary:
(someObject.someMethodReturningConstValue()).someNonConstMethod(); // error, calls non const method on const temporary
so you should only use it if you want to disallow calling non-const methods on temporaries. On the other hand, it kills move-semantics in C++11 so is discouraged.
The original question is related to overloading operator= and I like to share my findings as it was nontrivial for me to find them.
I cannot imagine reasonable example to use (a=b) as lvalue.
With the help of IRC and google I've found the next article:
http://msdn.microsoft.com/en-us/magazine/cc301415.aspx
it provides two examples.
(a=b)=c
f(T& );
f(a=b)
but both a bit not good, and I believe that it is bad practice.
The second one give me the same feeling.
Could you provide more good examples why it should be non constant?
One good reason is that one of the requirements in the standard for a class X to be useable in the standard containers is that the expression a = b must have type X& (where a is an lvalue of type X and b is an rvalue of type X).
Most probably because this is how the native types of the language work. e.g.:
int x = 0, y = 1, z = 2;
(x = y) = z;
AFAIK, Dr. Stroustrup said that it is a good thing to have consistency in the language. i.e. user-defined types should behave just like native types.
I've spent some time and here is my example:
class A
{
public:
const A& operator= (const A& a) {return *this;}
};
int main(int argc, char* argv[])
{
A a1;
A& a2 = a1;
A& a3 = (a2 = a1);
}
and the compiler output:
: error C2440: 'initializing' : cannot convert from 'const A' to 'A &'
I've checked it on MS VS 2010, but is it true on other platforms?
And if this example is sufficient condition for = to be non const?
Why should it be const? If you're assigning to it, obviously it's modifiable. That would be artifically limiting.
As for use cases, why not:
T &local = t1 = t2 = t3;
In this example, local isn't const.
Andrew Koenig wrote a post about this a long time ago. A lot of it comes down to doing what people expect under slight unusual circumstances. The example he gives is that in C, return x=y; always means the same thing as x=y; return x;. In C++, if you return essentially anything other than a reference (including a const reference), the two can mean different things.
Edit: Sorry, I linked to the wrong post. Try this one. The problem arises from the fact that a T const & can bind to a temporary instead of the "real" object, so what happened with the code above was that it created a temporary, copied the object into it, bound the reference to it, destroyed the temporary, then returned the (now dangling) reference.
If we consider three auto_ptr a, b and c, the operator = has to return a non-const reference so that you can do multiple assigns since assigning the pointer to another modifies the first.
so if we have a = b = c, the following happens:
c is assigned to b (c is modified to point to null), the operator returns a reference to b
the reference returned by (b = c) is assigned to a, it is thus modified to point to null, which is only possible if the reference is non-const.