iarray<T>& operator = (iarray<T>& v)
Why the return type is iarray<T>& not iarray<T> ?
UPDATE
Can someone elaborate in great detail why iarray<T> const &v ?
Because you don't want to return a copy of the array just for the sake of chaining. Returning a reference is a better option as it doesn't result in a copy being constructed.
Because then you can chain them efficiently, eg.
a = b = c;
See the C++ FAQ.
Why the return type is iarray& not iarray ?
Because the result of an assignment is a reference to what just got assigned. For example, the result of a = b should be a reference to a so you can chain them together like in c = a = b; which is effectively a = b; c = a;. (Yes, people like doing this on rare occasions; no, I don't know why it's such a hardship to break it into two lines.) Thus your code should look like:
iarray<T>& iarray<T>::operator = (const iarray<T>& v)
{
// ... copy `v`'s state over this object's ...
return *this;
}
Can someone elaborate in great detail why iarray const &v ?
Because an assignment operation has no business changing the right-hand side; it is unexpected behavior. If you have some funky thing in your object that needs to change, like a reference count, then you should prefer declaring that one part mutable over disallowing const right-hand side expressions. You can pass a non-const value in for a const parameter, but the reverse is not true.
Related
So this is really just something I can't make sense of semantically. Assignment chaining make sense for copy semantics:
int a, b, c{100};
a = b = c;
a, b, and c all are 100.
Try a similar thing with move semantics? Just doesn't work or make sense:
std::unique_ptr<int> a, b, c;
c = std::make_unique<int>(100);
a = b = std::move(c);
This doesn't even compile, because a = b is a copy assignment, which is deleted. I could make an argument that after the final expression executes, *a == 100 and b == nullptr and c == nullptr. But this isn't guaranteed by the standard. This doesn't change much:
a = std::move(b) = std::move(c);
This still involves copy assignment.
a = std::move(b = std::move(c));
This one actually does work, but syntactically it is a huge deviation from copy assignment chaining.
Declaring an overloaded move assignment operator involves returning an lvalue reference:
class MyMovable
{
public:
MyMovable& operator=(MyMovable&&) { return *this; }
};
But why isn't it an rvalue reference?
class MyMovable
{
public:
MyMovable() = default;
MyMovable(int value) { value_ = value; }
MyMovable(MyMovable const&) = delete;
MyMovable& operator=(MyMovable const&) = delete;
MyMovable&& operator=(MyMovable&& other) {
value_ = other.value_;
other.value_ = 0;
return std::move(*this);
}
int operator*() const { return value_; }
int value_{};
};
int main()
{
MyMovable a, b, c{100};
a = b = std::move(c);
std::cout << "a=" << *a << " b=" << *b << " c=" << *c << '\n';
}
Interestingly this example actually works as I expect, and gives me this output:
a=100 b=0 c=0
I'm not sure if it shouldn't work, or why it does, especially since formally move assignment isn't defined this way. Quite frankly this just adds more confusion in an already-confusing world of semantic behaviors for class types.
So I've brought up a few things here, so I'll try to condense it into a set of questions:
Are both forms of assignment operators valid?
When would you use one or the other?
Is move assignment chaining a thing? If so, when/why would you ever use it?
How do you even chain move assignment that returns lvalue references in a meaningful way?
Yes, you are free to return whatever you want from an overloaded assignment operator and so your MyMovable is fine, but users of your code may be confused by the unexpected semantics.
I don't see any reason to return a rvalue-reference in the move assignment. A move assignment usually looks like this:
b = std::move(a);
and after that, b should contain the state of a, while a will be in some empty or unspecified state.
If you chain it in this way:
c = b = std::move(a);
then you would expect b to not loose its state, because you never applied std::move to it. However if your move assignment operator returns by rvalue-reference, then this will actually move the state of a into b and then the left-hand assignment operator would also call the move assignment, transferring the state of b (which was a's before) to c. This is surprising, because now both a and b have empty/unspecified state. Instead I would expect a to be moved into b and copied into c, which is exactly what happens if the move assignment returns a lvalue-reference.
Now for
c = std::move(b = std::move(a));
it works as you expect, calling the move assignment in both cases and it would be clear that the state of b is moved as well. But why would you want to do that? You could have transferred a's state to c directly with c = std::move(a); without clearing b's state in the process (or worse putting it in a not directly usable state). Even if you want that, it would be clearer stated as a sequence
c = std::move(a);
b.clear(); // or something similar
As for
c = std::move(b) = std::move(a)
at least it is clear that b is moved from, but it seems as if the current state of b was moved, rather than the one after the right-hand move and again, the double move is redundant. As you noticed this still calls the copy-assignment for the left-hand, but if you really want to make this call the move assignment in both cases, you can return by rvalue-reference and to avoid the issue with c = b = std::move(a) explained above, you would need to differentiate on the value category of the middle expression. This can be done e.g. in this way:
MyMovable& operator=(MyMovable&& other) & {
...
return *this;
}
MyMovable&& operator=(MyMovable&& other) && {
return std::move(operator=(std::move(other)));
}
The & and && qualifiers signify that the particular overload should be used if the expression that the member function is called on is a lvalue or rvalue.
I don't know whether there is any case you would actually want to do that.
Are both forms of assignment operators valid?
They are well-formed and have well defined behaviour. But returning an rvalue reference to *this from a function that isn't rvalue qualified would be non-conventional and probably not a good design.
It would be very surprising if:
a = b = std::move(c));
caused b to be moved from. Surprisingness is not a good feature for an API.
When would you use one or the other?
In general, you'd never want to return an rvalue reference to *this except from rvalue qualified function. From rvalue qualified function, it's preferable to return an rvalue reference or maybe even a prvalue depending on context.
Is move assignment chaining a thing? If so, when/why would you ever use it?
I've never seen it used, and I cannot think of an attractive use case.
How do you even chain move assignment that returns lvalue references
Like you showed, with std::moves.
in a meaningful way?
I'm not sure if there is a way to introduce meaning to it.
Are both forms of assignment operators valid?
assignment operator are mostly regular method and doesn't need to return lvalue reference to self type, returning void, char would also be valid.
To avoid surprise, we try to mimic built-in and so to allow chaining assignment, we return lvalue reference to self type.
When would you use one or the other?
I would personally only use MyMovable& operator=(MyMovable const&)
Is move assignment chaining a thing? If so, when/why would you ever use it?
I don't thing so.
but to allow syntax a = std::move(b) = std::move(c);, you might do:
MyMovable& operator=(MyMovable const&) = delete;
MyMovable&& operator=(MyMovable&& other) &&;
MyMovable& operator=(MyMovable&& other) &;
Are both forms of assignment operators valid?
According to the standard, I believe so, but be very careful. When you return an rvalue reference from operator=, you're saying that it can be freely modified. This can easily result in surprising behaviour. For example,
void foo(const MyMovable& m) { }
void foo(MyMovable&& m) {
m.value_ = 666; // I'm allowed to do whatever I want to m
}
int main() {
MyMovable d;
foo(d = MyMovable{ 200 }); // will call foo(MyMovable&&) even though d is an lvalue!
std::cout << "d=" << *d << '\n'; // outputs 666
}
The proper way to fix this would probably be to define your operator= like this:
MyMovable&& operator=(MyMovable&& other) && {...
Now this only works if *this is already an rvalue, and the example above will not compile since it's using operator= on an lvalue. However, this then doesn't allow your chaining move operators to work. I'm not sure how to allow both move operator chaining whilst defending against behaviour like in my example above.
When would you use one or the other?
I don't think I'd ever return an rvalue reference to *this. It's prone to surprising the caller, as in the example above, and I'm not really sure that enabling move assignment chaining is something I would really want to do.
Is move assignment chaining a thing? If so, when/why would you ever use it?
I don't ever use assignment chaining. It saves a few characters but I think it makes the code less obvious and, as this question demonstrates, can be a little tricky in practice.
How do you even chain move assignment that returns lvalue references in a meaningful way?
If I had to do this, I would use the form you used above: a = std::move(b = std::move(c)); This is explicit and obvious.
I am new to C++. I need some help understanding this code snippet.
Queue & operator=(const Queue &rhs)
{
front = rhs.front;
nWaiting = rhs.nWaiting;
for (int i = front, j = 0; j < nWaiting; j++)
{
elements[i] = rhs.elements[i];
i = (i + 1) % 100;
}
return *this;
}
I am unable to understand why there is an '&' before operator in the code and how does this work along with *this.
I understand operator overloading. For eg. the code below for addition operation overloading. However I don't understand why '&' is required for assignment operator (=) overloading.
V3 operator* (const double factor, const V3 &b)
{
return (b * factor);
}
The reference means that avoid copying the object. As a result, it will return a reference to the same object. Moreover, it will provide lvalue as a result. And if you think about it, that's what you want to happen when the assignment operator is used.
Every object in C++ has access to its own address through this pointer.
That means that the you return the object itself.
If your question is why we use *this instead of this, then this happens because you need to dereference the pointer first, since the return type is a reference (and not a pointer for example).
The & means the operator returns a reference (the original object), instead of a value (a copy of the object). This avoids unnecessary copying. this is a pointer to the object itself which the operator is called on, so return *this means return a reference to the object on the left side of the =.
This allows for the operator to be chained, like a = b = 1. This assigns 1 to b first, and a reference to b is returned. The value of b then gets assigned to a. So a and b both are 1.
The operator could be fine without any return value, however it is common to enable chaining as in
c = (a = b);
This will assign b to a and then assign the return value of the operator= call to c. As you dont want to make a unneccesary copy you return a reference to the object itself aka *this. Actually avoiding a copy is not the only reason for using a reference, but if you consider
(d = e) = f;
then this will only work as expected (first assigning e to d and then assigning f to d) if operator= returns a non-const (!) reference.
Note that operator* is different, because it is not supposed to modify the object it is invoked on but rather it returns a new instance (hence no & in the return of operator*).
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.
I'm doing some exploration of operator-overloading at the moment whilst re-reading some of my old University text-books and I think I'm mis-understanding something, so hopefully this will be some nice easy reputation for some answerers. If this is a duplicate please point me in the right direction.
I've created a simple counter class that has (at this stage) a single member, val (an int).
I have initialised three of these counters, varOne to varThree, and want the third counter to be the sum of the first two (e.g. varThree.val is set to 5 in the below code)
counter::counter(int initialVal)
{
val = initialVal;
//pVal = new int;
//*pVal = 10; // an arbitrary number for now
}
int main (int argc, char const* argv[])
{
counter varOne(3), varTwo(2), varThree;
varThree = varOne + varTwo;
return 0;
}
I've overloaded operator+ like so:
counter operator+(counter& lhs, counter& rhs)
{
counter temp(lhs.val + rhs.val);
return temp;
}
I've made this a non-member function, and a friend of the counter class so that it can access the private values.
My problem starts when adding another private member, pVal (a pointer to an int). Adding this means that I can no longer do a simple varThree = varOne copy because when varOne is destroyed, varThree.pVal will still be pointing to the same bit of memory.
I've overloaded operator= as follows.
int counter::getN()
{
return *newVal;
}
counter& counter::operator=(counter &rhs)
{
if (this == &rhs) return *this;
val = rhs.val;
delete pVal;
pVal = new int;
*pVal = rhs.getN();
return *this;
}
Now if I do something like varThree = varOne everything copies correctly, however trying to do varThree = varOne + varTwo gives me the following error:
counter.cpp: In function ‘int main(int, const char**)’:
counter.cpp:96: error: no match for ‘operator=’ in ‘varThree = operator+(counter&, counter&)(((counter&)(& varTwo)))’
counter.cpp:55: note: candidates are: counter& counter::operator=(counter&)
make: *** [counter] Error 1
It looks as though counter::operator= is having trouble coping with the return output from operator+, and that I need to overload operator= further to accept the type that operator+ is returning, but I've had no luck and I'm beginning to think that maybe I've done something fundamentally wrong.
You need to pass your parameters as const reference. For example:
counter& counter::operator=( const counter &rhs )
And similarly for operator+(). This is necessary in order to be able to bind temporary values to the function parameter(s). Temporary values are created when you return by value, so when you say:
varOne + varTwo
a nameless temporary is created. This is the right thing to do, but you have to make sure that functions such as the assignment op can accept such values by making their parameters const.
You also need to implement the copy constructor and destructor for your class, though lack of these will not cause compilation errors (unfortunately).
Another way to approach this problem is to use the PImpl pattern and swap for the assignment operator. Assuming that you still have a counter(int) constructor you could write operator= as follows
counter& counter::operator=(const counter& rhs) {
counter temp(rhs.getN());
std::swap(&pVal,rhs.pVal);
return *this;
}
This has the benefit of leaving the messy memory management functions in the constructor and destructor where they should be.
The key here (as touched upon by a previous poster) but worth emphasizing is that expressions in C++ can be categorized as being either rvalues or lvalues.
Their is much detail behind those categories, but a useful heuristic to guide your intuition is: if you can take the address of an expression (such as a variable) it is an lvalue (there is much more to the story here, but this is a good place to start).
If it is truly not an lvalue, it is an rvalue - and a useful heuristic for rvalues is that they represent "hidden" temporary objects that the compiler instantiates to make your code work. These objects are created and destroyed by the compiler behind the scenes.
Why is this relevant here?
Well, in C++98/03 (which is what i presume you are using), remember the following two rules:
1) Only lvalue expressions can bind to non-const references (ignoring casts)
2) rvalue expressions can bind to only const references (ignoring casts)
An example will help here:
// Consider the function foo below - it returns an int -
// whenever this function is called, the compiler has
// to behave as if a temporary int object with the value 5 is returned.
// The use of 'foo()' is an expression that is an rvalue - try typing &foo() -
// [Note: if foo was declared as int& foo(), the story gets complicated, so
// i'll leave that for another post if someone asks]
int foo() { return 5; }
void bind_r(int& r) { return; }
void bind_cr(const int& cr) { return; }
int main()
{
int i = 10; // ok
int& ri = i; // ok binding lvalue to non-const reference, see rule #1
int& ri2 = foo(); // Not ok, binding a temporary (rvalue) to a non-const reference
// The temporary int is created & destroyed by compiler here
const int& cri = foo(); // ok - see rule #2, temporary int is NOT destroyed here
//Similarly
bind_r(i); // ok - rule #1
bind_r(foo()); // NOT ok - rule #2
bind_cr(foo()); // ok - rule #2
// Since the rules above keep you out of trouble, but do not exhaust all possibilities
// know that the following is well-formed too:
const int& cri2 = i;
bind_cr(i);
bind_cr(cri);
bind_cr(cri2);
}
When you bind an rvalue to a const reference, you basically extend the temporary object's life-time to the life-time (in this case scope) of the reference (and the compiler can not just destroy it at the end of that expression) - so you end up with a reference to a valid object.
I hope this helps in understanding why you have to declare your assignment operator as accepting a const reference and not just a non-const reference, as one of the other posters rightly recommended.
p.s. There are some other issues with your code (such as why you are destroying and creating the memory that your object exclusively points to upon each assignment, and the lack of a copy constructor and destructor), and if no one has addressed them by the time this post appears, i will :)
p.s. It may also be worth knowing that C++0x adds something known as rvalue references (non-const) that preferentially bind to rvalues and provide for extremely powerful optimization opportunities for the programmer (without having to rely on the optimization capabilities of the compiler) - they also help solve the problem of creating the perfect forwarding function in C++ - but now we're digressing ;)