Destruction of string temporaries in thrown exceptions - c++

Consider the following code:
std::string my_error_string = "Some error message";
// ...
throw std::runtime_error(std::string("Error: ") + my_error_string);
The string passed to runtime_error is a temporary returned by string's operator+. Suppose this exception is handled something like:
catch (const std::runtime_error& e)
{
std::cout << e.what() << std::endl;
}
When is the temporary returned by string's operator+ destroyed? Does the language spec have anything to say about this? Also, suppose runtime_error took a const char* argument and was thrown like this:
// Suppose runtime_error has the constructor runtime_error(const char* message)
throw std::runtime_error((std::string("Error: ") + my_error_string).c_str());
Now when is the temporary string returned by operator+ destroyed? Would it be destroyed before the catch block tries to print it, and is this why runtime_error accepts a std::string and not a const char*?

runtime_error is a class which contains a string. That string will be managed for you by the normal C++ construction and destruction mechanisms. If it contained a char *, then that would have to be explicitly managed, but you would still not have to do anything as a user of runtime_error.
Despite what you may read elsewhere on the internet, C++ is designed to almost always do "the reasonable thing" - you actually have to try fairly hard to break this reasonable behaviour, though of course it is not impossible to do so.

As a temporary object (12.2), the result of the + will be destroyed as the last step in the evaluation of the full-expression (1.9/9) that contains it. In this case the full-expression is the throw-expression.
A throw-expression constructs a temporary object (the exception-object) (15.1) (std::runtime_error in this case). All the temporaries in the throw-expression will be destroyed after the exception-object has been constructed. The exception is thrown only once the evaluation of the throw-expression has completed, as the destruction of temporaries is part of this evaluation they will be destroyed before the destruction of automatic variables constructed since the try block was entered (15.2) and before the handler is entered.
The post-condition on runtime_error's constructor is that what() returns something that strcmp considers equal to what c_str() on the passed in argument returns. It is a theoretical possiblility that once the std::string passed as a constructor argument is destroyed, runtime_error's what() could return something different, although it would be a questionable implementation and it would still have to be a null-terminated string of some sort, it couldn't return a pointer to a stale c_str() of a dead string.

Note that the runtime_error exception class makes a copy of the string passed into the constructor. So when you're calling .what() on the exception object, you're not getting back the same exact string instance you passed in.
So to answer your question, the temporary you're asking about gets destroyed "at the semicolon" of the expression that contains it (this is true in both your first and second version of the question), but as I said, this isn't that interesting, because a copy of it was already made.

Related

When do rvalue objects get destroyed when accessing their members as part of a function call?

I've just been thinking about the following bit of code:
PerformConflict(m_dwSession,
CONFLICT_DETECTED,
item.GetConflictedFile().GetUnNormalizedPath().c_str(),
item.GetSuggestedFile().GetUnNormalizedPath().c_str());
GetConflictFile() returns an object.
GetUnNormalizedPath()
returns a std::wstring
c_str() just returns a const wchar_t* (in this case to the contents of an rvalue std::wstring)
My question is: Does anything in the spec guarantee that this code is safe? I.e. are all the rvalue objects guaranteed not to have been destroyed by the time that c_str() is getting a pointer to their contents?
Those temporaries will be destroyed at the end of the full expression they appear in. In your case, that's the entire snippet you posted.
This will be absolutely fine, so long as you only use that const wchar_t* inside that function invocation. If you store it anywhere and try to access it after the call exits, you would be thrust down the deep dark hole of UB.
The relevant standards quote is (emphasis mine):
N3337 [class.temporary]/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 ef f ects of destroying
a temporary object are associated only with the full-expression, not with any specific subexpression.
As illustrated by Herb Sutter, rvalues are destroyed at the end of the expression in which they appear. However, if you bind them to "a reference to const on the stack", their lifetime is extended to that of the reference.
So, basically, if your function has this kind of signature:
PerformConflict(...,
...,
const std::string& str1, //< any rvalue passed here will have the same lifetime as str1
const std::string& str2 //< any rvalue passed here will have the same lifetime as str2
);
You should be able to manipulate the strings inside PerformConflict() without problems.
PS: the problem can also be solved if you pass the arguments by value (i.e. const std::string str1)

c++ exceptions throw by value catch by reference

In C++ when throwing object by value like: throw Exception(), this will create temp object, how can it be caught by reference? i know it works, but if it was a function return value or function call it would have failed without adding const to type, what is the difference ?
First, when you write
throw Exception();
what's being thrown isn't actually the temporary object created by the prvalue expression Exception(). Conceptually, there's a separate object - the exception object - that's initialized from that temporary object, and it is the exception object that's actually thrown. (Compilers are allowed to elide the copy/move, though.)
Second, the language rules say that the exception object is always considered an lvalue. Hence it is allowed to bind to non-const lvalue references.

What's the scope of this string?

If I have the following code:
{
UnicodeString sFish = L"FISH";
char *szFish = AnsiString(sFish).c_str();
CallFunc(szFish);
}
Then what is the scope of the temporary AnsiString that's created, and for how long is szFish pointing to valid data? Will it still be valid for the CallFunc function?
Will it's scope last just the one line, or for the whole block?
szFish is invalid before the call to CallFunc(), because AnsiString is a temporary object that is destructed immediately and szFish is pointing to its internal buffer which will have just been deleted.
Ensure that the AnsiString instance is valid for the invocation of CallFunc(). For example:
CallFunc(AnsiString(sFish).c_str());
I would replace:
char *szFish = AnsiString(sFish).c_str();
with:
AnsiString as(sFish);
char *szFish = as.c_str();
I don't know the AnsiString class but in your code its destructor will fire before your call to CallFunc(), and will most probably release the string you point to with *szFish. When you replace the temporary object with a "named" object on stack its lifetime will extend until the end of the block it is defined in.
The C++11 standard $12.2.3 says:
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 a temporary object are associated only with the
full-expression, not with any specific subexpression.
(emphasis mine)
There are additional caveats to this, but they don't apply in this situation. In your case the full expression is the indicated part of this statement:
char *szFish = AnsiString(sFish).c_str();
// ^^^^^^^^^^^^^^^^^^^^^^^^^
So, the instant szFish is assigned, the destructor of your temporary object (i.e. AnsiString(sFish)) will be called and its internal memory representation (where c_str() points to) will be released. Thus, szFish will be immediately become a dangling pointer and any access will fail.
You can get around this by saying
CallFunc(AnsiString(sFish).c_str());
instead, as here, the temporary will be destroyed (again) after the full expression (that is, right at the ;) and CallFunc will be able to read the raw string.
The scope of the AnsiString in this case is "from right before the call to c_str(), until right after."
It may help to think of it this way:
char *szFish;
{
AnsiString tmpString(sFish);
szFish = tmpString.c_str();
}

Content of char pointer seems to get deleted while being passed to the catch block

I encountered a very strange (at least to me) behaviour of a exception class I have thrown. What I do is that I allocate memory via new for a string in the constructor of the exception class and fill it with characters. So far everything is fine. When debugging the code I can see in Visual Studio that the pointer actually has the right content.
Now the weird thing happens. My next breakpoint is in the catch - block to which the exception is passed after being constructed and here I can see in the debugger that the content of the string contained in the exception object is severly corrupted. Even though the address didn't change at all! So it seems like the content of the string gets destructed.
So I put a breakpoint into the exceptions destructor and really, it is being called before the catch - block is entered. This confuses me a lot since I learned to pass exceptions by reference to the catch block. But what good is that if the destructor gets called before I can access the dynamically created data...
I constructed a minimal example that shows the situation I am in:
#include <iostream>
#include <cstring>
class test_exception {
public:
test_exception();
~test_exception() {
delete[] _msg;
}
// Getter Functions
char* errorMessage() const {
return _msg;
}
private:
char* _msg;
};
test_exception::test_exception()
{
_msg = new char[22];
strcpy(_msg, "This is a test string");
}
int main(int argc, char* argv[])
{
try {
throw test_exception();
} catch (const test_exception& err) {
std::cout << err.errorMessage() << std::endl;
}
std::cin.get();
return 0;
}
It would be create if someone could tell me if it is weird MS behaviour or if I misunderstood how try - catch - blocks should be used.
Exceptions are copied (or in C++11, possibly moved) when they're thrown. Quoting C++11, §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. 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 or the operand of a return statement.
Because test_exception violates the rule-of-three (or for C++11, the rule-of-five), test_exception::_msg has already been deleted by the time you enter your catch block.
Since exceptions are copied, you should add a copy constructor to your test_exception object. The exception thrown is not the same one as the one received by the catch.

Catch by reference when exception variable is not defined

When catching an exception the standard guidance is to throw by value, catch by reference. As I understand it, this is for two reasons:
If the exception was thrown due to an out of memory exception, we won't call a copy constructor which could potentially terminate the program.
If the exception is part of an inheritance heirarchy, we may potentially have object slicing on the exception.
If we have a scenario where we don't define the exception name in the catch block are these concerns (really 1., as slicing won't be an issue if we don't have a name for the variable) still valid?
For example:
catch(my_exception)
{ ... }
or
catch(my_exception &)
{ ... }
Is there still the possibility of the program terminating if the exception caught by value in this case? My feeling is that it is technically still possible.
Note: I am asking this because I have had to review someone's code who put a catch by value in this case. As shown in the question I am not entirely sure on the technical impact of either choice, but I think that in terms of consistancy it is better to catch by reference in this case regardless (there is no downside to catching by reference in any case).
The standard does not require special optimization in the case of an unnamed exception object. On the contrary, it then requires an effect as if a temporary is copy-initialized. This copying can result in memory being dynamically allocated.
N3290 §15.3/16:
If the exception-declaration specifies a name, it declares a variable which is copy-initialized (8.5) from the
exception object. If the exception-declaration denotes an object type but does not specify a name, a temporary (12.2) is copy-initialized (8.5) from the exception object. The lifetime of the variable or temporary
ends when the handler exits, after the destruction of any automatic objects initialized within the handler.
The paragraph above does not mention catching by reference, and so one might reasonably conclude that it applies whether or not the exception object is caught by reference; that a copy is constructed anyway.
However, that is contradicted by the next paragraph:
N3290 §15.3/17:
When the handler declares a non-constant object, any changes to that object will not affect the temporary
object that was initialized by execution of the throw-expression. When the handler declares a reference to
a non-constant object, any changes to the referenced object are changes to the temporary object initialized
when the throw-expression was executed and will have effect should that object be rethrown.
So, declared type T& (with T non-const) is the single case where C++11 requires a reference directly to the thrown object, instead of copying. And it is also that way in C++03, except that C++03 has some additional wording about as-if optimization. So, for the formal the preference should be for
catch( T& name )
and
catch( T& )
However, I have always caught exceptions like catch( T const& ). From a practical point of view one may assume that the compiler will optimize that to a direct reference to the thrown object, even though it is possible to devise cases where the observed program effect would then be non-standard. For example <evil grin>
#include <stdio.h>
#include <stdexcept>
struct Error
: std::runtime_error
{
public:
static Error* pThrown;
char const* pMessage_;
Error()
: std::runtime_error( "Base class message" )
, pMessage_( "Original message." )
{
printf( "Default-construction of Error object.\n" );
pThrown = this;
}
Error( Error const& other )
: std::runtime_error( other )
, pMessage_( other.pMessage_ )
{
printf( "Copy-construction of Error obejct.\n" );
}
char const* what() const throw() { return pMessage_; }
};
Error* Error::pThrown = 0;
int main()
{
printf( "Testing non-const ref:\n" );
try
{
throw Error();
}
catch( Error& x )
{
Error::pThrown->pMessage_ = "Modified message.";
printf( "%s\n", x.what() );
}
printf( "\n" );
printf( "Testing const ref:\n" );
try
{
throw Error();
}
catch( Error const& x )
{
Error::pThrown->pMessage_ = "Modified message";
printf( "%s\n", x.what() );
}
}
With both MinGW g++ 4.4.1 and Visual C++ 10.0 the output is …
Testing non-const ref:
Default-construction of Error object.
Modified message.
Testing const ref:
Default-construction of Error object.
Modified message
A pedantic formalist might say that both compilers are non-conforming, failing to create a copy for the Error const& case. A purely practical practitioner might say that hey, what else did you expect? And me, I say that the wording in the standard is very far from perfect here, and that if anything, one should expect a clarification to explicitly allow the output above, so that also catching by reference to const is both safe and maximally efficient.
Summing up wrt. the OP's question:
Catching by reference won’t call a copy constructor which could potentially terminate the program.
The standard only guarantees this for reference to non-const.
In practice, as shown, it is also guaranteed for reference to const, even when program results are then affected.
Cheers & hth.,
I would prefer catching by reference. The compiler could discard the exception as an optimization and do no copy, but that's only a possibility. Catching by reference gives you certainties.
There might be invisible data associated with your exception, e.g. a vtable.
The vtable is an invisible data structure that carries information about where certain, polymorphic (i.e. virtual) member functions can be found. This table in the general case costs a bit of memory that is held in the object itself. This may by the size of a pointer into some external table, or even the complete table. As always, it depends.