Throw a temporary argument passed by reference - c++

inline void my_assert( bool cond, const std::exception &e = my_assert_failed() )
{
if ( !cond )
throw e;
}
The standard ensures that:
A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full expression containing the call.
And for a thrown temporary object:
The temporary persists as long as there is a handler being executed for that exception.
Can I infer that a temporary that is passed to my_assert survives until the catch block finishes?

From N4296 (first draft after final C++14) [15.1p3]:
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).
So you can't assume that your temporary "survives the throw". If throwing, the copy constructor of an exception object of type std::exception will be called with e as the argument. The temporary that e is bound to will be destroyed when control leaves the full expression containing the call to my_assert (either after a normal return or as part of stack unwinding, since you're conditionally throwing the exception).
There are circumstances when the copy construction of the exception object can be elided, but this is not one of them, according to [12.8p31.2]:
— in a throw-expression (5.17), when the operand is the name of a
non-volatile automatic object (other than a function or catch-clause
parameter) whose scope does not extend beyond the end of the innermost
enclosing try-block (if there is one), the copy/move operation from
the operand to the exception object (15.1) can be omitted by
constructing the automatic object directly into the exception object
(emphasis mine)

Related

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.

Some questions on life span of a string object

Trying to understand c++ string a little more here. Compiler here is g++ 4.7.3.
Question 1: In the following code snippet, will the compiler be smart enough to free the user data (in the heap, implicitly pointed to by s) at the end of the function? I think the answer is yes, just want to confirm.
void dummy() {
string s;
s.append("hello");
//more append
}
Question 2: in the following snippet, compiler will not free the user data pointed to by s when the function returns. Is that right? If so, the user data may be freed in the caller by the compiler (if the caller function itself doesn't return the string object).
string dummy() {
string s;
s.append("hello");
//more append
return s;
}
//some caller
string s1 = dummy();
In question 1, yes the memory for the variable s will be freed when the function dummy ends because that was the enclosing scope of the s variable.
In question 2, the compiler will likely use return value optimization to avoid having to copy the memory from s into s1, since s is about to fall out of scope.
Question 1: In the following code snippet, will the compiler be smart enough to free the user data (in the heap, implicitly pointed to by s) at the end of the function?
Yes, it is required to. At the end of the function scope, the string's destructor will be called, which will free the memory allocated.
The object s is said to have "automatic storage duration", and it's described by the standard via §3.7.3/1:
Block-scope variables explicitly declared register or not explicitly declared static or extern have au- tomatic storage duration. The storage for these entities lasts until the block in which they are created exits.
Question 2: in the following snippet, compiler will not free the user data pointed to by s when the function returns. Is that right? If so, the user data may be freed in the caller by the compiler (if the caller function itself doesn't return the string object).
The compiler may elide the copy there, using what's called RVO (return value optimization). Specifically the standard words this in the following way, in §12.8/31:
This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv- unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
in a throw-expression (5.17), when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object
when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
when the exception-declaration of an exception handler (Clause 15) declares an object of the same type (except for cv-qualification) as the exception object (15.1), the copy operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration. [ Note: There cannot be a move from the exception object because it is always an lvalue. —endnote]

C++: when object constructed in argument is destructed?

When object constructed in argument is destructed, before of after function call?
E.g. is the following code safe?
void f(const char*)
{ ... }
std::string g()
{ ... }
...
f(g().c_str());
It always works for me but I don't know is it just undefined behaviour or it actually should work.
g() is a temporary. Lifetime of tempraries extends for the entire time of evaluation of the entire full-expression (in your case, that would be f(g().c_str())) Therefore your usage is safe, unless the f() stores the pointer somewhere.
§12.2/4 There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when an expression appears as an initializer for a declarator defining an object. In that context, the temporary that holds the result of the expression shall persist until the object’s initialization is complete. [...]
§12.2/5 The second context is when a reference is bound to a temporary. [...]
Neither of these two cases apply in your example.
A temporary object constructed as part of an expression evaluation is destructed after evaluating the full expression containing the expression, unless it is bound to a named reference. (12.2 and 1.9 in the current draft standard are the relevant sections).
So in your example, the temporary constructed to hold the return value of g will be destroyed after f returns.
It always works for me but I don't know is it just undefined behaviour
or it actually should work.
No, there is no undefined behavior because temporary object produced by g() will be deleted after evaluating full expression, that is body of f() function.
C++ Standard n3337 § 12.3/3
When an implementation introduces a temporary object of a class that has a non-trivial constructor (12.1,
12.8), it shall ensure that a constructor is called for the temporary object. Similarly, the destructor shall be
called for a temporary with a non-trivial destructor (12.4). 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. The value computations and side effects of destroying
C++ Standard n3337 § 12.3/4
There are two contexts in which temporaries are destroyed at a different point than the end of the full-
expression. The first context is when a default constructor is called to initialize an element of an array. If
the constructor has one or more default arguments, the destruction of every temporary created in a default
argument is sequenced before the construction of the next array element, if any.
C++ Standard n3337 § 12.3/5
The second context is when a reference is bound to a temporary. (...)

Why g++ does not enable RVO here?

Consider that TEST code:
#include <iostream>
using namespace std;
class Klass
{
public:
Klass()
{
cout << "Klass()" << endl;
}
Klass(const Klass& right)
{
cout << "Klass(const Klass& right)" << endl;
}
};
Klass create(Klass a)
{
cout << "create(Klass a)" << endl;
return a;
}
int main()
{
const Klass result = create(Klass());
}
Compiling with:
g++ -O3 rvo.cpp -o rvo
The output is:
$ ./rvo
Klass()
create(Klass a)
Klass(const Klass& right)
I was expecting the compiler to use the RVO mechanism in order elide every COPY CTOR call, to avoid copying the return value AND the parameter of the function create(). Why isn't it the case?
The standard allows copy elision only in case where you pass a temporary as a function argument.
The two elisions you're expecting are bolded below:
[C++11: 12.8/31]: When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object
when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
when the exception-declaration of an exception handler (Clause 15) declares an object of the same type (except for cv-qualification) as the exception object (15.1), the copy/move operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration. [..]
It didn't happen for the return value because the non-volatile name was a function parameter.
It has happened for the construction into create's parameter, otherwise you'd have seen:
Klass()
Klass(const Klass& right)
create(Klass a)
Klass(const Klass& right)
The copy you see is a copy for the "return" statement in the "create" function. It cannot be eliminated by RVO, as it is not possible to construct the return value directly. You requested to "return a". A copy is needed here; there is no way to return an object without it.
In a standard speak, following condition of [C++11: 12.8/31] is not met
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
As for the reasons, it is not an arbitrary rule, it makes sense from implementation point of view, as this is what is not possible to do with a function parameters:
constructing the automatic object directly into the function’s return value
You are copying the function parameter. You cannot elide this copy without inlining, as the parameter already exists before you enter the function, therefore you cannot construct that object into the return value directly instead.

What is the difference between throw and throw with arg of caught exception?

Imagine two similar pieces of code:
try {
[...]
} catch (myErr &err) {
err.append("More info added to error...");
throw err;
}
and
try {
[...]
} catch (myErr &err) {
err.append("More info added to error...");
throw;
}
Are these effectively the same or do they differ in some subtle way? For example, does the first one cause a copy constructor to be run whereas perhaps the second reuses the same object to rethrow it?
Depending on how you have arranged your exception hierarchy, re-throwing an exception by naming the exception variable in the throw statement may slice the original exception object.
A no-argument throw expression will throw the current exception object preserving its dynamic type, whereas a throw expression with an argument will throw a new exception based on the static type of the argument to throw.
E.g.
int main()
{
try
{
try
{
throw Derived();
}
catch (Base& b)
{
std::cout << "Caught a reference to base\n";
b.print(std::cout);
throw b;
}
}
catch (Base& b)
{
std::cout << "Caught a reference to base\n";
b.print(std::cout);
}
return 0;
}
As written above, the program will output:
Caught a reference to base
Derived
Caught a reference to base
Base
If the throw b is replace with a throw, then the outer catch will also catch the originally thrown Derived exception. This still holds if the inner class catches the Base exception by value instead of by reference - although naturally this would mean that the original exception object cannot be modified, so any changes to b would not be reflected in the Derived exception caught by the outer block.
In the second case according to C++ Standard 15.1/6 copy constructor is not used:
A throw-expression with no operand rethrows the exception being handled. The exception is reactivated with the existing temporary; no new temporary exception object is created. The exception is no longer considered to be caught; therefore, the value of uncaught_exception() will again be true.
In the first case new exception will be thrown according to 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 used to initialize the variable named in the matching handler (15.3). The type of the throw-expression shall not be an
incomplete type, or a pointer or reference to an incomplete type, other than void*, const void*,
volatile void*, or const volatile void*. 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.
In both cases copy constructor is required at throw stage (15.1/5):
When the thrown object is a class object, and the copy constructor used to initialize the temporary copy is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated).
Similarly, if the destructor for that object is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated).