I have a custom class MyInt, encapsulating a int m_nValue data. The postfix operator
MyInt operator++(int)
{
MyInt temp(*this);
++(*this);
return temp;
}
if the operator returns an object, then why i am not able to call postfix++ operator more than once like:
MyInt value(10);
value++; // valid
value++++; // Why is this invalid?? Cant it be interpreted as (value++)++
Why does value++++ gives an error lvalue required
if i can call value.print() method defined in MyInt Class, then i should also be able to do value++++?
Wrong answer:
Because value++ is a temporary variable that holds the old value of value. You can't ++ it.
You also can't write 15++! It's similar. The former is a temporary variable, the later is a constant, none of which you can increment.
Correction: Since this answer got accepted, I am not going to change the original post, but since people will read it, I will post the correction here.
First off, I am not saying constants and temporary objects are the same concept. I was just trying to say temporary objects are not l-values, like constants are not, without actually using the word l-value.
About value++++, it is not an error. I just tested it with my g++ 4.5 and it works fine. In other answers you can read:
From section 3.10.10 of the 2003 standard:
An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [Example: a member function called for an object (9.3) can modify the object. ]
What is inherently wrong about what you are doing is this:
Let's say value holds 10. The first value++ changes value to 11 but returns a temporary object containing 10. Then you ++ it which changes the temporary value (that you never access) to 11, but again returns a temporary object that contains 10. So value++++ behaves exactly like value++ except it does some unnecessary work.
Actually, this should work:
#include <iostream>
struct MyInt {
MyInt() : value(0) {}
MyInt& operator++() {
std::cout << "Inside MyInt::operator++()" << std::endl;
++value;
return *this;
}
MyInt operator++(int)
{
MyInt temp(*this);
++(*this);
return temp;
}
int value;
};
int main() {
MyInt mi;
std::cout << "Value before: " << mi.value << std::endl;
mi++++;
std::cout << "Value after: " << mi.value << std::endl;
}
This operator is basically just a normal member-function with fancy syntax, and as such you can invoke it on an rvalue. Note how, as Martinho explains in his comment, the effect is not the desired one, because the second increment operates on a temporary.
From section 3.10.10 of the 2003 standard:
An lvalue for an object is necessary in order to modify the object
except that an rvalue of class type can also be used to modify its
referent under certain circumstances. [Example: a member function
called for an object (9.3) can modify the object. ]
Related
I'm having some trouble understanding how pointer dereferencing in C++ works. Let's look at this simple example:
struct Value {
int x = 0;
void Inc() { x++; }
};
int main(int argc, char* argv[]) {
Value* v = new Value();
v->Inc();
std::cout << v->x << std::endl; // prints 1, as I would expect
(*v).Inc();
std::cout << v->x << std::endl; // prints 2, but I would have expected it to print 1,
// as I thought (*v) would create a local copy of
// the original `Value` object.
Value v2 = *v;
v2.Inc();
std::cout << v->x << std::endl; // prints 2, as I would expect
I'm a bit confused here. I would assume that the 2nd and 3rd calls to Inc() would be equivalent. Namely, that (*v).Inc() would unfold into a temporary variable holding a copy of v on the stack, and that Inc() would then increment that copy on the stack of v instead of the original v. Why is that not the case?
Thanks
In the (*v).Inc(); statement, the LHS of the . operator is the result of the indirection of the v pointer. This will be an lvalue expression referring to the object to which v points. From this Draft C++ Standard (emphasis mine):
8.5.2.1 Unary operators [expr.unary.op]
1 The unary *
operator performs indirection: the expression to which it is applied
shall be a pointer to an object type, or a pointer to a function type
and the result is an lvalue referring to the object or function to
which the expression points.
So, in this first case, no temporary object need be created and the Inc() function is called on the original Value object created by the new operation.
However, in this statement: Value v2 = *v;, you are declaring a separate Value object and initialising it with a copy of the Value pointed to by v. Thus, any subsequent modifications to v2 will not affect the object referred to by v.
*pointer just returns an object the pointer points to, quoting [expr.unary.op]/1:
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points
Value v2 = *v is a form of initialisation, so it actually calls a constructor. This would be equivalent to Value v2{ *v } (for this particular class).
For the part why *pointer doesn't create a temporary, there are well-defined rules on when temporaries are created:
Temporary objects are created when a prvalue is materialized so that
it can be used as a glvalue, which occurs (since C++17) in the
following situations:
binding a reference to a prvalue
initializing an object of type
std::initializer_list from a braced-init-list (since C++11)
returning a prvalue from a function
conversion that creates a prvalue
(including T(a,b,c) and T{})
lambda expression (since C++11)
copy-initialization that requires conversion of the initializer,
reference-initialization to a different but convertible type or to a
bitfield.
plus some others scenarios for C++17. For this particular case the most important part is that indirection returns an lvalue, so there is no rule applicable to it if the expression doesn't partake in any other expression.
Using the return value of operator* from a "dead" unique_ptr is bad.
The following code compiles but results of course in Undefined Behavior:
auto& ref = *std::make_unique<int>(7);
std::cout << ref << std::endl;
Why didn't the standard make the return type of operator* for an rvalue of std::unique_ptr an rvalue of the internal value, instead of an lvalue, like this:
// could have been done inside unique_ptr
T& operator*() & { return *ptr; }
T&& operator*() && { return std::move(*ptr); }
In which case this would work fine:
std::cout << *std::make_unique<int>(7) << std::endl;
But the code at the beginning would not compile (cannot bind an rvalue to an lvalue).
Side note: of course someone could still write bad code like the below, but it is saying "I'm UB" more verbosely, IMHO, thus less relevant for this discussion:
auto&& ref = *std::make_unique<int>(7);
std::cout << ref << std::endl;
Is there any good reason for operator* on an rvalue of std::unique_ptr to return an lvalue ref?
Your code, in terms of the value categories involved and the basic idea, is the equivalent of this:
auto &ref = *(new int(7));
new int(7) results in a pointer object which is a prvalue expression. Dereferencing that prvalue results in an lvalue expression.
Regardless of whether the pointer object is an rvalue or lvalue, applying * to a pointer will result in an lvalue. That shouldn't change just because the pointer is "smart".
Good question!
Without digging into the relevant papers and design discussions, I think there are a few points that are maybe the reasons for this design decision:
As #Nicol Bolas mentioned, this is how a built-in (raw) pointer would behave, so "do as int does" is applied here as "do as int* does".
This is similar to the fact that unique_ptr (and other library types) don't propagate constness (which in turn is why we are adding propagate_const).
What about the following code snippet? It doesn't compile with your suggested change, while it is a valid code that shouldn't be blocked.
class Base { virtual ~Base() = default; };
class Derived : public Base {};
void f(Base&) {}
int main()
{
f(*std::make_unique<Derived>());
}
(godbolt - it compiles if our operator* overloadings are commented out)
For your side note: I'm not sure auto&& says "I'm UB" any louder. On the contrary, some would argue that auto&& should be our default for many cases (e.g. range-based for loop; it was even suggested to be inserted automatically for "terse-notation range-based for loop" (which wasn't accepted, but still...)). Let's remember that rvalue-ref has similar effect as const &, extension of the lifetime of a temporary (within the known restrictions), so it doesn't necessarily look like a UB in general.
std::cout << *std::make_unique<int>(7) << std::endl; already works as the temporary dies at the end of the full expression.
T& operator*() & { return *ptr; }
T&& operator*() && { return std::move(*ptr); }
wouldn't avoid the dangling reference, (as for your example)
auto&& ref = *std::make_unique<int>(7); // or const auto&
std::cout << ref << std::endl;
but indeed, would avoid binding a temporary to a non-const lvalue reference.
Another safer alternative would be:
T& operator*() & { return *ptr; }
T operator*() && { return std::move(*ptr); }
to allow the lifetime extension, but that would do an extra move constructor not necessarily wanted in the general case.
Returning reference to this object is often used in assignment operator overloading. It is also used as a base for named parameters idiom which allows to initialize object by chain of calls to setter methods: Params().SetX(1).SetY(1) each of which returns reference to *this.
But is it correct to return reference to *this. What if we call the method returning reference to this for a temporary object:
#include <iostream>
class Obj
{
public:
Obj(int n): member(n) {}
Obj& Me() { return *this; }
int member;
};
Obj MakeObj(int n)
{
return Obj(n);
}
int main()
{
// Are the following constructions are correct:
std::cout << MakeObj(1).Me().member << std::endl;
std::cout << Obj(2).Me().member << std::endl;
Obj(3).Me() = Obj(4);
return 0;
}
Yes, it is safe to return *this. The easy case is when this is not a temporary, though even when it is, this should be possible:
Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. This is true even if that evaluation ends in throwing an exception (C++03 §12.2/3).
In other words, until you reach a semi-colon everything should be fine (in theory).
So following code should work:
std::cout << MakeObj(1).Me().member << std::endl;
While this should not work:
const Obj &MakeMeObj(int n) { return Obj(n).Me(); }
std::cout << MakeMeObj(1).member << std::endl;
This is logical, as you are returning a reference to a temporary. Most compilers warn/error on this, though if you code gets to complex, this is something to watch out for.
Personally, I would prevent calling these methods on a temp object to enforce API users to think about the lifetime of the object. Which can be done by overloading your method: (If your compiler supports it already)
Obj &Me() & { return *this; }
Obj &Me() && = delete;
// Are the following constructions are correct:
std::cout << MakeObj(1).Me().member << std::endl;
std::cout << Obj(2).Me().member << std::endl;
Yes, because in each line the lifetime of all temporary objects is extended to take the full expression into account.
As cppreference.com says:
(...) all temporary objects are destroyed as the last step in
evaluating the full-expression that (lexically) contains the point
where they were created (...).
If you try to split up the full expression, then you will (hopefully) get a compiler error or warning:
// not allowed:
Obj& ref = MakeObj(1);
std::cout << ref.Me().member << std::endl;
In other cases, the compiler may not be smart enough to see the problem, create your executable without giving any diagnostic message, and ultimately building undefined behaviour into your program:
// undefined behaviour:
Obj &ref = MakeObj(1).Me();
std::cout << ref.member << std::endl;
Yes, this is safe. The temporary object's life will be until the end of the statement (more precisely the evaluation of the full expression in which it is created). This is guaranteed by the standard:
12.2/3: Temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the
point where they were created.
Temporary lifetime may even be extended under some conditions if bound to a reference. But don't expect miracles here. Trying to keep the reference beyond the statement (f.ex by taking the address or assigning a reference) could quickly lead to UB (demo).
If you'd use this kind of construct on const objects you'd also have some troubles as you'd try to return a non const ref (but this isn't relevant in your examples for assignment and setters).
Regarding this: Why does std::move prevent RVO? someone wrote that: "Hence in a return statement copy elision can only occur, if the expression is the name of a local variable"
However I made a little test with GCC:
class X
{
public:
X()
{
cout << "def" << endl;
}
X(const X& x)
{
cout << "copy" << endl;
}
X(X&& x)
{
cout << "move" << endl;
}
};
X produceX()
{
return X();
}
int main()
{
X x{produceX()};
return 0;
}
The produceX function does not return a named value. It returns an unnamed temporary object. However the RVO still kicks and there is no copy nor move construction. The x object from main is constructed in-place. If I write produceX like this:
X produceX()
{
X localNamedObject;
return localNamedObject;
}
it behaves the same way (which is expected). But why RVO is allowed in the former case?
That statement is an over-simplification, although the answer you took it from does in fact answer this question, and provides the relevant text from the standard.
Copy elision is allowed when returning a temporary (as a general case of using a temporary to initialise an object of the same type), as well as when returning a local variable.
It's also allowed when throwing and catching exceptions by value, but that's beyond the scope of this question.
RVO stands for "return value optimization" and refers to the technique of constructing the return expression result directly within the return value space. It is applied when the return expression is an rvalue.
NRVO stands for "named return value optimization" and refers to the technique of constructing the named object that will eventually be returned directly within the return value space. It is applied when the return expression is an lvalue.
I have the following code:
class Array
{
public:
int aaa;
Array():aaa(1){}
void print()
{
cout << aaa << endl;
}
Array& operator++()
{
aaa++;
return *this;
}
Array operator++(int)
{
Array a(*this);
aaa++;
return a;
}
};
I have some questions as follows:
why prefix returns a reference and postfix returns an object? In the book C++ Primer, the author only explained "For consistency with the built-in operators".
Then, I tested the code:
Array ar;
(ar++).print(); // print 1
ar.print(); // print 2
the output is exactly what I expected.
Now I changed the code in the overloading postfix function as:
Array operator++(int)
{
Array a(*this);
a.aaa++; // changed this
return a;
}
I called the test code:
Array ar;
(ar++).print(); // this prints 2
ar.print(); // this prints 1
Why I got such results?
The postfix operator returns an object, not a reference, because it has to return an unchanged version of the current object; it has to return the value before the increment is done. Therefore a new object must be allocated. If you returned a reference, what would it be a reference to?
In your second example, you're creating a new object, incrementing it, and returning it, but you're not changing the original object that the operator was applied to -- this is clearly wrong, so gives wrong results.
While both prefix and postfix operators would intuitively seem to mutate the objects on which they're invoked, they actually have different semantic meanings. The prefix operator takes an an object, applies the increment operation to it, and returns the same object. The postfix operator takes an object, makes a copy of it, applies the increment operator to the orginal, and returns the copy.
It's for this reason that you may have seen various sources discouraging the use of the postfix operator when possible -- because the postfix copy creates a temporary object, it may be less efficient than the prefix operator. For an object that has to maintain a lot of state information, using the postfix operator can be expensive.