Teilhart
Teilhart

Reputation: 119

C++11: does returning objects by value never throw exceptions when a move ctor is defined?

In C++11 and later standards, is it guaranteed that the (possibly exception-throwing) copy ctor is not called when returning a class object by value from a function - provided a move ctor is defined for this class? Background: Suppose

struct X {
   X() {}
   X(const X&) {/* code that might throw exceptions */}
   X(X&&) {/* code that never throws exceptions */}
   ...
};

and

X my_func(some_type& t)
{
   X x;
   // code that modifies t and x but never throws exceptions
   return x;
}

Now, does, for instance, an expression such as

some_other_func(my_func(t));

never throw exceptions (i.e., is this guaranteed?) provided the function some_other_func(const X&) does not throw exceptions? And what if the signature of some_other_func were some_other_func(X)?

Upvotes: 4

Views: 197

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275740

Exceptions are never spontaneously generated in a well formed C++ program that only excecutes defined behavior.

If your code is a well formed program, you never call or interact with a language or library feature that is permitted to throw exceptions, and the code you write not throw exceptions (explicitly), then the result is code that won't throw exceptions.

Unless you mark your functions and methods as noexcept, the compiler and standard library may not realize this, and may assume your code throws exceptions (even though it cannot). In some cases this could cause the compiler to allocate memory and throw an exception out of caution that your code could throw an exception, or call a different method (copy instead of move for example in certain vector operations).

What you should do is mark X(X&&) and X() as noexcept, and this particular problem goes away.

Despite this, your exact code won't throw, unless some_other_function doesn't take a X but instead a type created from an X and that operation could throw, or the body of some_other_function could throw.

Upvotes: 1

The rule is simple: a function is treated as if it can throw exceptions unless it's marked as a function which cannot. A move constructor is a function like any other in this respect. When defined manually, it is considered potentially throwing unless defined noexcept.

Implicitly defaulted functions (i.e. the auto-provided copy & move operations) generally follow the logic of: declared as noexcept if and only if all functions which it calls are noexcept.

Upvotes: 4

Related Questions