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.
Related
When using a '&' next to the return type, are you returning a reference, or is it a bit-by-bit copy still?
Example:
T& operator[](const int index)
{
return m_array[index];
}
There is no copy made of m_array[index] - the return is a reference variable to it.
You are never "returning a reference". This is a general sloppiness of expression by people who hopefully know better, but if you're new or unsure, it pays to be accurate: What really happens is that evaluating a function produces a value. Values have types, and those types are always object types (i.e. never references). So a function cannot "return a reference"; a function always "returns a value", if you will. (But it's better to say that "function evaluation produces a value".)
The only question is what that value is. If a function is declared as U f(), where U is an object type, then the value is a temporary, which is passed around by copy (at least nominally). However, if the function is declared as U & f() or U && f(), then the value is some existing object, and no new object is created and passed around. Such function evaluation lets you see some existing object directly, if you will. Colloquially we say that "f returns a reference", but be careful with such language.
In your case, m_array is an existing object, m_array[index] is (presumably) some subobject of that object, and the function evaluation produces that very object (which is presumably some array element).
When using a '&' next to the return type, are you returning a reference, or is it a bit-by-bit copy still?
You are returning a reference to whatever object the function's return statement specifies (so that shouldn't be a temporary, which the leading m_ implies it's not - all good).
Still, returning references to objects is not the opposite of "bit-by-bit copy"... C++ often uses custom copy constructors and/or assignment operators to ensure proper deep copying etc..
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.
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.
According to the C++ FAQ, when one throws an object, it's thrown using the static type of the expression. Hence, if you have:
catch ( some_exception const &e ) {
// ...
throw e; // throws static type, possibly causing "slicing"; should just "throw;" instead
}
and e is actually a reference to some class derived from some_exception, the above throw will cause the object to be "sliced" silently. Yes, I know the correct answer is simply to throw;, but the way things are seems like an unnecessary source of confusion and bugs.
What's the rationale for this? Why wouldn't you want it to throw by the dynamic type of the object?
When you throw something, a temporary object is constructed from the operand of the throw and that temporary object is the object that is caught.
C++ doesn't have built-in support for copying things or creating objects based on the dynamic type of an expression, so the temporary object is of the static type of the operand.
The "argument" to throw is an expression and it is the type of the expression that determines the type of the exception object thrown. The type of the expression thrown doesn't necessarily have to be a polymorphic type so there may not be a way to determine if the expression actually refers to a base class subobject of a more derived type.
The simpler "type of the expression" rule also means that the implementation doesn't have to dynamically determine the size and type of the exception object at runtime which might require more complex and less efficient code to be generated for exception handling. If it had to do this it would represent the only place in a language where a copy constructor for a type unknown at the call point was required. This might add significantly to the cost of implementation.
Consider that we can have references to objects where the static type of the reference is copyable, but the dynamic type of the object is not.
struct foo {};
struct ncfoo : foo
{
private:
ncfoo(ncfoo const&) {}
};
ncfoo g_ncfoo;
void fun()
{
foo& ref = g_ncfoo;
throw ref; // what should be thrown here?
}
If you say "in this case just throw the static type", then how are the exact rules - what does "in this case" mean? References we just caught are "re-thrown" without copying, everything else is copied? Hm...
But however you define the rule, it would still be confusing. Throwing by-reference would lead to different behavior, depending on where we got that reference from. Neh. C++ is already complicated and confusing enough :)
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.