Binding temporaries to non-const references in case of exceptions - c++

I have always read that temporaries are allowed to bind only with non-const reference arguments in case of function calls..
CASE 1:-
For example:-
class Simple{
public:
int i;
Simple(Simple &f)
{
i = f.i + 1;
}
Simple(int j)
{
i = j;
}
};
int main()
{
Simple f1 = Simple(2); // error no matching call fruit::fruit(fruit)...
return 0;
}
This would give me error as I am trying to bind temporary with non-const reference arguments.
CASE 2:-
try
{
throw e;
}
catch ( exception& e )
{
}
I have learnt when we throw an exception what really get passed to catch is the copy of original exception thrown i.e a temporary is created for the object thrown and then this would be passed to catch clause.
What catch is doing is catching this exception by non-const reference. This is in contrast to what I have shown in CASE 1.
So, my questions are:-
1) Are there specific scenarios where binding temporary to non-const reference is allowed.
2) If there are then what factors are taken into account while allowing these exceptions.

Are there specific scenarios where binding temporary to non-const reference is allowed.
For lvalue-references (that is, the type T&) there isn't. You can't bind a temporary to a non-const lvalue-reference because it doesn't make much sense to modify, say, a literal like 42. They can bind to const lvalue-references because then a promise has been made not to modify the object to which the reference is bound.
They can in fact bind to rvalue-references (T&&), but that's unrelated to this thread.
If there are then what factors are taken into account while allowing these exceptions.
It is true that temporaries cannot bind to non-const lvalue-references, but there's a certain provision made for exception objects:
Taken from the C++11 standard (closest draft n3337):
§15.1/3 A throw-expression initializes a temporary object, called the
exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw
and adjusting the type from “array of T” or “function returning T” to
“pointer to T” or “pointer to function returning T”, respectively. The
temporary is an lvalue and is used to initialize the variable named in
the matching handler (15.3). [..]
emphasis mine
cppreference simplifies this into:
Unlike other temporary objects, the exception object is considered to be an lvalue argument when initializing the catch clause parameters, so it can be caught by lvalue reference, modified, and rethrown.
So for this case you can in fact bind the exception to a non-const lvalue-reference.

Related

rvalue reference (expression) returned by function is xvalue - but no identity?

According to What are rvalues, lvalues, xvalues, glvalues, and prvalues? and some other explanations, my understanding is that xvalue is the expression which has identity and is safely moved (or is so marked).
Some texts like this and this say that, if a function f()'s return type is rvalue reference, then the expression f() is xvalue. For example:
int&& f() {
return 1;
}
int main() {
f(); // xvalue
2; // prvalue
}
My confusion is that, because the origin of f() is the literal 1, for me f() doesn't seem to have an identity and thus I can't understand how it becomes xvalue. If 1 has identity, why is 2 said to have no identity and is prvalue? Does prvalue suddenly have "identity" if it's returned from a function as an rvalue reference?
EDIT
It's pointed out that f() returns a dangling reference, but I hope my point still makes sense.
EDIT2
Well, after reading the (very helpful) comments, it seems that it probably doesn't make sense?
Does prvalue suddenly have "identity" if it's returned from a function as an rvalue reference?
Yes, actually. The standard pretty much says that outright:
[conv.rval]
A prvalue of type T can be converted to an xvalue of type T. This conversion initializes a temporary object ([class.temporary]) of type T from the prvalue by evaluating the prvalue with the temporary object as its result object, and produces an xvalue denoting the temporary object.
That temporary object, while it exists, most certainly has "identity". Of course, the result of such a conversion is no longer a prvalue, so perhaps we shouldn't say the prvalue "gets an identity." Note that this works, too, also because of temporary materialization:
(int&&)1; // This is different from f(), though, because that reference is dangling but I believe this one isn't (lifetime extension of a temporary by binding to a reference applies here but is suppressed for a return)
Note that the operand of a return statement and the thing that actually gets returned simply don't have to be the same thing. You give an int prvalue, you need an int xvalue, the return makes it work by materializing a temporary. It's not obliged to fail because of the mismatch. Unfortunately, that temporary immediately gets destroyed when the return statement ends, leaving the xvalue dangling, but, for that moment in between the returned reference being bound and the temporary being destroyed, yes, the rvalue reference indeed referred to an object with its own identity.
Other examples of prvalues being materialized so you can bind references to them:
int &&x = 1; // acts just like int x = 1 except for decltype and similar considerations
int const &y = 1; // ditto but const and also available in older C++ versions
// in some imaginary API
void install_controller(std::unique_ptr<controller> &&new_controller) {
if(can_replace_controller()) current_controller = std::move(new_controller);
}
install_controller(std::make_unique<controller>("My Controller"));
// prvalue returned by std::make_unique materializes a temporary, binds to new_controller
// might be moved from, might not; in latter case new pointer (and thus object)
// is destroyed at full-expression end (at the semicolon after the function call)
// useful to take by reference so you can do something like
auto p = std::make_unique<controller>("Perseverant Controller");
while(p) { wait_for_something(); install_controller(std::move(p)); }
Other examples of return not being trivial:
double d(int x) { return x; }
// int lvalue given to return but double prvalue actually returned! the horror!
struct dangerous {
dangerous(int x) { launch_missiles(); }
};
dangerous f() { return 1; }
// launch_missiles is called from within the return!
std::vector<std::string> init_data() {
return {5, "Hello!"};
}
// now the operand of return isn't even a value/expression!
// also, in terms of temporaries, "Hello!" (char const[7] lvalue) decays to a
// char const* prvalue, converts to a std::string prvalue (initializing the
// parameter of std::string's constructor), and then that prvalue materializes
// into a temporary so that it can bind to the std::string const& parameter of
// std::vector<std::string>'s constructor
Here I try to summarize my understanding after reading the given comments.
The whole purpose of returning an rvalue reference is to use it in some way, so returning an rvalue reference that points to a function local object, which is already invalid when the function returns, is not considered (well, I'm sure the committee does consider this of course, but not as an intended usage).
As a result, if I have a function T&& f() { /.../ return val; }, val is supposed to locate somewhere with its identity even after f() returns, otherwise it's dangling which is a mere error. Therefore, the intention that f() has an identity, so is an xvalue, is justified.
To be honest, I find the whole concept of "having identity" somewhat moot.
Here's how I tend to think about it:
A prvalue is an expression that creates an object.
An rvalue is an expression denoting a temporary object (or an object considered to be temporary, e.g. because it was std::moved).
An lvalue is an expression denoting a non-temporary object (or an object considered to be non-temporary).
A call to int &&f() {...} doesn't create a new object (at least if we ignore the function body, and only look at the function-calling mechanism itself), so the result is not a prvalue (but it's obviously an rvalue, thus it's also an xvalue).
A call to int f() {...}, on the other hand, unconditionally creates an object (the temporary int; regardless of the function body), so it's a prvalue.

Lifetime extension, prvalues and xvalues

Following the well accepted answer to this question Do rvalue references allow dangling references? It would seem that xvalues do not have their lifetime extended when assigned to a rvalue reference lvalue like in the question. However when I do this
#include <iostream>
using namespace std;
class Something {
public:
Something() {
cout << "Something()" << endl;
}
Something(const Something&) {
cout << "Something(const Something&)" << endl;
}
Something(Something&&) {
cout << "Something(Something&&)" << endl;
}
~Something() {
cout << "~Something()" << endl;
}
int a;
};
Something make_something() {
return Something{};
}
int main() {
auto&& something = make_something().a;
return 0;
}
The lifetime of the object returned by a call to make_something is extended, even though make_something().a is an xvalue as per http://en.cppreference.com/w/cpp/language/value_category (the third bullet in the xvalues explanation lists the member access I have above as an xvalue,)
a.m, the member of object expression, where a is an rvalue and m is a
non-static data member of non-reference type;
If value categories do not determine when the lifetime of an rvalue will be extended then what does? I am having a hard time understanding when the lifetime of an rvalue is extended in C++
Lifetime extension doesn't care about value categories. As stated by [class.temporary]/p6:
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference
Emphasis added.
This says nothing here about the value category of the expression being referenced.
What determines whether a temporary is extended is exactly the above (and a few more rules).
But that does not explain why adding an std::move() around the temporary to which the reference is being assigned does not extend the lifetime
std::move is not a magical, compiler-defined construct in C++. It is a function call, and therefore it behaves no differently from any other C++ function call.
So, if you have std::move(Type()), what does that mean? It means that you will create a temporary, bind it to the parameter of std::move, then call that function, which will return something.
Binding a temporary to a function parameter, as stated in [class.temporary]/p6, means that the lifetime of the temporary is fixed to be the lifetime of the full expression that created it (if not for that rule, then the temporary would have to be destroyed at the end of the function call, since that's the end of the reference's lifetime).
It doesn't matter what the function does, says, or implies. It doesn't matter if the compiler could perhaps inline things and determine that the return value is a reference to an argument that came from a temporary. The lifetime of that temporary is fixed to the expression, not extended.
If value categories do not determine when the lifetime of an rvalue will be extended then what does? I am having a hard time understanding when the lifetime of an rvalue is extended in C++
Note that value categories describe expressions not objects. Value categories ( xvalue, prvalue, or whatever ) won't be extended in any way. Only objects can have a lifetime.
From the n4296 standard draft:
§12.2.1
Temporaries of class type are created in various contexts: binding a reference to a prvalue (8.5.3), returning
a prvalue (6.6.3), a conversion that creates a prvalue (4.1, 5.2.9, 5.2.11, 5.4), throwing an exception (15.1),
and in some initializations (8.5).
and
§12.2.4
There are two contexts in which temporaries are destroyed at a different point than the end of the full-
expression. [...]
The second context is when a reference is bound to a temporary. The temporary to which the reference is
bound or the temporary that is the complete object of a subobject to which the reference is bound persists
for the lifetime of the reference
Note: I didn't quote the first context since it has minor revelance regarding the questions. Italic emphasis added by me.
Thus the value category of the function expression makesomething() is a prvalue creating a temporary of class type, according to the first paragraph cited above.
makesomething().a accesses a temporary, complete subobject. Binding this temporary to a reference leads, according to the second context quoated above, to an extended lifetime.
The lifetime of the subobject a is coupled to the lifetime of the previous created temporary making it an expiring value (xvalue). Without extending its lifetime by binding it to a reference it would be destroyed together with the temporary class object. Thus, in this case, after the ;.

C++ throwing class members

I have the following C++ code
template <class E>
class ExceptionWrapper {
public:
explicit ExceptionWrapper(const E& e): e(e) {}
void throwException() {
throw e;
}
private:
E e;
};
...
try {
ExceptionWrapper<E> w(...);
w.throwException();
} catch (const E& e) {
...
}
...
Question: is this code valid? I could argue that returning a reference to the class member is almost always not valid (and I am sure everybody agrees with this statement). However, my colleague claims that this is not the case with throw.
P.S. after changing catch (const E& e) to catch (E e) a nasty bug seemingly disappeared which strengthens my position - that this code is not valid.
my claim is that catching e by reference is not valid since e is a member of w and w is not alive in the catch scope.
Your claim is incorrect. throw e; throws a copy of the member, and that copy is valid in catch's scope.
§ 15.1 / 3 (n3797 draft):
Throwing an exception copy-initializes (
8.5
,
12.8
) a temporary object, called the
exception object
. The
temporary is an lvalue and is used to initialize the variable named in the matching
handler
(
15.3
). If the
type of the exception object would be an incomplete type or a pointer to an incomplete type other than
(possibly cv-qualified)
void
the program is ill-formed. Evaluating a
throw-expression
with an operand throws
an exception; the type of the exception object is determined by removing any top-level
cv-qualifiers
from the
static type of the operand and adjusting the type from “array of
T
” or “function returning
T
” to “pointer to
T
” or “pointer to function returning
T
,” respectively.
Catching by const reference is the preferred way to catch exceptions. It allows catching derivatives of std::exception without slicing the exception object.
I think the relevant point is :
15.1. Throwing an exception:
p3. A throw-expression initializes a temporary object, called the exception object, the type of which is determined
by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type
from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”, respectively.
The temporary is an lvalue and is used to initialize the variable named in the matching handler (15.3). If
the type of the exception object would be an incomplete type or a pointer to an incomplete type other
than (possibly cv-qualified) void the program is ill-formed. Except for these restrictions and the restrictions
on type matching mentioned in 15.3, the operand of throw is treated exactly as a function argument in a
call (5.2.2) or the operand of a return statement.
This is from the draft for c++11, emphasis mine.
It basically means there is a temporary object created from the argument of throw. Just like there was a function E f(){return private_e;}, and that temporary is used as the argument for the appropriate handler. So you would have two possible copies actually if you didn't catch by reference.
Probably also relevant:
p5. When the thrown object is a class object, the copy/move constructor and the destructor shall be accessible,
even if the copy/move operation is elided (12.8).
If the constructor fails/throws, w doesn't exist. So "w.throwException()" is not valid.

Throwing an rvalue

Consider the fragment:
try {
Foo f;
throw std::move(f);
}
catch (Foo& f) { }
[expr.throw] says that:
the type of the exception object
is determined by removing any top-level cv-qualifiers from the static type of the operand and adjusting the
type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”,
respectively.
which would be Foo&&. The exception object is then initialized according to [except.throw]:
Throwing an exception copy-initializes (8.5, 12.8) a temporary object, called the exception object. The
temporary is an lvalue and is used to initialize the variable declared in the matching handler (15.3). If the
type of the exception object would be an incomplete type or a pointer to an incomplete type other than
(possibly cv-qualified) void the program is ill-formed.
This suggests to me that the exception object is initialized as:
Foo&& __exception_object = std::move(f);
and that the handler would not match. However, both gcc and clang do catch this exception. So what's the actual type of the exception object here? If Foo, why?
The static type of an expression is never a reference type.
1.3.24 [defns.static.type] defines "static type":
type of an expression (3.9) resulting from analysis of the program without considering execution semantics
the first step in that "analysis of the program" is to remove references, see
5 [expr] p5 and Expressions can have reference type
If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.
So std::move(f) is an xvalue expression, with static type Foo.
You don't need to involve rvalues to demonstrate this, the same was true in C++03 with:
int& f();
throw f();
This throws an int not an int&.
Without considering the specifics, an exception object is an object, and a reference is not an object, so an exception object cannot be a reference. It must be an object.

Rvalues vs temporaries

Somebody generalized the statement "Temporaries are rvalues". I said "no" and gave him the following example
double k=3;
double& foo()
{
return k;
}
int main()
{
foo()=3; //foo() creates a temporary which is an lvalue
}
Is my interpretation correct?
Temporaries and rvalues are different (but related) concepts. Being temporary is a property of an object. Examples of objects that aren't tempory are local objects, global objects and dynamically created objects.
Being an rvalue is a property of an expression. The opposite of rvalues are lvalues such as names or dereferenced pointers. The statement "Temporaries are rvalues" is meaningless. Here is the relationsip between rvalues and temporary objects:
An rvalue is an expression whose evaluation creates a temporary object which is destroyed at the end of the full-expression that lexically contains the rvalue.
Note that lvalues can also denote temporary objects!
void blah(const std::string& s);
blah(std::string("test"));
Inside the function blah, the lvalue s denotes the temporary object created by evaluating the expression std::string("test").
Your comment "references are lvalues" is also meaningless. A reference is not an expression and thus cannot be an lvalue. What you really mean is:
The expression function() is an lvalue if the function returns a reference.
No. You are returning a reference to an global double, not a temporary.
The same test with a real temporary would be:
double foo() { return 3.0; }
int main() {
foo() = 2.0; // error: lvalue required as left operand of assignment
}
EDIT:
The answer was meant just to identify that the example was wrong, and I did not really want to get into the deeper discussion of whether temporaries are or not rvalues... As others have said, lvalue-ness or rvalue-ness are properties of an expression and not of the object (in the most general sense, not only class instances). Then again, the standard says that:
§3.10/5 The result of calling a function that does not return a reference is an rvalue. User defined operators are functions, and whether such operators expect or yield lvalues is determined by their parameter and return types.
§3.10/6 An expression which holds a temporary object resulting from a cast to a nonreference type is an rvalue (this includes the explicit creation of an object using functional notation (5.2.3)).
Which AFAIK are the circumstances under which temporaries are created. Now, it is also true that you can bind a constant reference to a temporary, in which case you will get a new variable (the reference) that can be used as an lvalue that effectively refers to the temporary object.
The fine line is that expressions that create temporaries are rvalue expressions. You can bind a constant reference to the result of that expression to obtain a variable that can be used as an const-qualified lvalue expression.
Temporaries were so consistently protected from becoming lvalues, that they are now called rvalues. But C++0x will allow temporaries to become lvalues thanks to move semantics. Like in this dumb snippet
void blah(ICanBeTemporary && temp)
{
temp.data = 2; //here temporary becomes lvalue
}
//somewhere
blah(ICanBeTemporary("yes I can"));
Now we have terminology mess. People used to call temporaries rvalues and this is called rvalue reference. Named objects are now considered to be non-rvalue referenced.