Does an exception use move semantics when thrown in C++11? - c++

http://www.drdobbs.com/cpp/practical-c-error-handling-in-hybrid-env/197003350?pgno=4
In this article Herb Sutter explains that throwing an exception requires a copy of the exception as it's created as a temporary and therefore uses an std::auto_ptr to get round the copy overhead. In light of move semantics being made available in C++11 is this still necessary?

I have just checked, and the Standard allows
omitting the copy or move of an object specified by the operand of a throw expression into the exception object
omitting the copy or move of the exception object into the catch clause variable of the same type as the exception object if you don't otherwise change the meaning of the program (i.e if you would rethrow and subsequent catches would suddenly see a changed exception object changed by the previous catch block).
Since these omissions are allowed, the spec requires to first regard the source of the copy or move as an rvalue. So this means that the respective objects will be moved if possible. Of course copy and move elision are still allowed as the first choice.
Update
I was notified that the consideration of the exception object initializer of a catch clause parameter as an rvalue initializer will probably be dropped from the Standard (because in general it is not possible for all cases to detect when the behavior of the program is unchanged when omitting a copy/move), so I recommend to not rely on this (second bullet above).
What you can still rely about is the move of a local variable into the exception object, as in throw x; (first bullet above).

Move from exception objects is not mandatory now.
It's a defect of C++11. See CWG1493 .

Upon throw expression, a copy of the exception object always needs to be created as the original object goes out of the scope during the stack unwinding process.During that initialization, we may expect copy elision (see this) – omits copy or move constructors (object constructed directly into the storage of the target object). But even though copy elision may or may not be applied you should provide proper copy constructor and/or move constructor which is what C++ standard mandates(see 15.1). See below compilation error for reference.
But modern C++ provides more feature: "Moving safely with move semantics & exception"
struct demo
{
demo() = default;
demo(const demo &) { cout << "Copying\n"; }
// Exception safe move constructor
demo(demo &&) noexcept { cout << "Moving\n"; }
private:
std::vector<int> m_v;
};
int main()
{
demo obj1;
if (noexcept(demo(std::declval<demo>()))){ // if moving safe
demo obj2(std::move(obj1)); // then move it
}
else{
demo obj2(obj1); // otherwise copy it
}
demo obj3(std::move_if_noexcept(obj1)); // Alternatively you can do this----------------
return 0;
}
We can use noexcept(T(std::declval<T>())) to check if T’s move constructor exists and is noexcept in order to decide if we want to create an instance of T by moving another instance of T (using std::move). Alternatively, we can use std::move_if_noexcept, which uses noexcept operator and casts to either rvalue or lvalue. Such checks are used in std::vector and other containers.This will be useful while you are processing critical data which you don't want to lose. For example, we have critical data received from the server that we do not want to lose it at any cost while processing. In such a case, we should use std::move_if_noexcept which will move ownership of critical data only and only if move constructor is exception-safe.
From: 7 best practices for exception handling in C++

Related

Copy elision of arguments as return values

Given the following; will C++17 guarantee copy elision?
template <typename Widget>
Widget frobnicate(Widget w) {
// optionally mutate w in some way
return w;
}
Does answer change if Widget implements a move constructor or not?
Should I ever return by move? Such as, in this case:
return std::move(w);
No. We cannot have guaranteed copy elision from a function parameter. If you think about this from an implementation perspective - your argument w has to exist in a register or an address somewhere, so it cannot then be first constructed into the return slot.
Somewhat moot, given (1).
You should never†‡ write return std::move(w); because that actually would inhibit copy elision in the happy cases:
Widget frobnicate() {
Widget w;
return std::move(w); // oops
}
†Except there are actually a few unfortunate places in C++17 where you actually need to write std::move because there are places where objects aren't automatically moved-from for you. See P0527 and P1155 for several examples (the OP case - returning a function parameter - is not one of these; that one will be implicitly moved from even in C++17). C++20 will implicitly move from all of these situations as well, so then you really should never‡ write return std::move(w);
‡Of course, unless w isn't a function parameter or local variable with automatic storage duration. Then nothing in the language could safely assume that it could move it on your behalf, so you have to do so explicitly.
Copy ellision will only happen for variables instantiated in the method. Thar is due to how copy ellision. The caller will make space for the return value when it calls the callee. But in order to use that space, the callee will have to create a variable using this space in its definition (theoretically it maybe could direct the copy that is made in the paramter (since it is passed by value) to this space vut compilers aren't that good yet)) Source: A cppcon talk about copy ellision.
That a move constructor exists will not give you copy ellision, but if copy ellision is impossible, the compiler will first try to move and then to copy if move is impossible. So the existence of a move constructor will probably improve the speed if there is no copy ellision.
You should never return a temporary (i.e. a variable going out of scope at the end of the function) by std::move since it prevents copy ellision and even if the copy ellision is not possible, the compiler will move by default. The only reason (I can think of) to return by mkve is, if you are releasing a resource the object held before the call. For example std::unique_ptr::release should return by move, iirc.

Is it legal to implement assignment operators as "destroy + construct"?

I frequently need to implement C++ wrappers for "raw" resource handles, like file handles, Win32 OS handles and similar. When doing this, I also need to implement move operators, since the default compiler-generated ones will not clear the moved-from object, yielding double-delete problems.
When implementing the move assignment operator, I prefer to call the destructor explicitly and in-place recreate the object with placement new. That way, I avoid duplication of the destructor logic. In addition, I often implement copy assignment in terms of copy+move (when relevant). This leads to the following code:
/** Canonical move-assignment operator.
Assumes no const or reference members. */
TYPE& operator = (TYPE && other) noexcept {
if (&other == this)
return *this; // self-assign
static_assert(std::is_final<TYPE>::value, "class must be final");
static_assert(noexcept(this->~TYPE()), "dtor must be noexcept");
this->~TYPE();
static_assert(noexcept(TYPE(std::move(other))), "move-ctor must be noexcept");
new(this) TYPE(std::move(other));
return *this;
}
/** Canonical copy-assignment operator. */
TYPE& operator = (const TYPE& other) {
if (&other == this)
return *this; // self-assign
TYPE copy(other); // may throw
static_assert(noexcept(operator = (std::move(copy))), "move-assignment must be noexcept");
operator = (std::move(copy));
return *this;
}
It strikes me as odd, but I have not seen any recommendations online for implementing the move+copy assignment operators in this "canonical" way. Instead, most websites tend to implement the assignment operators in a type-specific way that must be manually kept in sync with the constructors & destructor when maintaining the class.
Are there any arguments (besides performance) against implementing the move & copy assignment operators in this type-independent "canonical" way?
UPDATE 2019-10-08 based on UB comments:
I've read through http://eel.is/c++draft/basic.life#8 that seem to cover the case in question. Extract:
If, after the lifetime of an object has ended ..., a new object is
created at the storage location which the original object occupied, a
pointer that pointed to the original object, a reference that referred
to the original object, ... will
automatically refer to the new object and, ..., can be used to manipulate the new object, if ...
There's some obvious conditions thereafter related to the same type and const/reference members, but they seem to be required for any assignment operator implementation.
Please correct me if I'm wrong, but this seems to me like my "canonical" sample is well behaved and not UB(?)
UPDATE 2019-10-10 based on copy-and-swap comments:
The assignment implementations can be merged into a single method that takes a value argument instead of reference. This also seem to eliminate the need for the static_assert and self-assignment checks. My new proposed implementation then becomes:
/** Canonical copy/move-assignment operator.
Assumes no const or reference members. */
TYPE& operator = (TYPE other) noexcept {
static_assert(!std::has_virtual_destructor<TYPE>::value, "dtor cannot be virtual");
this->~TYPE();
new(this) TYPE(std::move(other));
return *this;
}
There is a strong argument against your "canonical" implementation — it is wrong.
You end the lifetime the original object and create a new object in its place. However, pointers, references, etc. to the original object are not automatically updated to point to the new object — you have to use std::launder. (This sentence is wrong for most classes; see Davis Herring’s comment.) Then, the destructor is automatically called on the original object, triggering undefined behavior.
Reference: (emphasis mine) [class.dtor]/16
Once a destructor is invoked for an object, the object no longer
exists; the behavior is undefined if the destructor is invoked for an
object whose lifetime has ended. [ Example: If the destructor
for an automatic object is explicitly invoked, and the block is
subsequently left in a manner that would ordinarily invoke implicit
destruction of the object, the behavior is undefined.
— end example ]
[basic.life]/1
[...] The lifetime of an object o of type T ends when:
if T is a class type with a non-trivial destructor ([class.dtor]), the destructor call starts, or
the storage which the object occupies is released, or is reused by an object that is not nested within o ([intro.object]).
(Depending on whether the destructor of your class is trivial, the line of code that ends the lifetime of the object is different. If the destructor is non-trivial, explicitly calling the destructor ends the lifetime of the object; otherwise, the placement new reuses the storage of the current object, ending its lifetime. In either case, the lifetime of the object has been ended when the assignment operator returns.)
You may think that this is yet another "any sane implementation will do the right thing" kind of undefined behavior, but actually many compiler optimization involve caching values, which take advantage of this specification. Therefore, your code can break at any time when the code is compiled under a different optimization level, by a different compiler, with a different version of the same compiler, or when the compiler just had a terrible day and is in a bad mood.
The actual "canonical" way is to use the copy-and-swap idiom:
// copy constructor is implemented normally
C::C(const C& other)
: // ...
{
// ...
}
// move constructor = default construct + swap
C::C(C&& other) noexcept
: C{}
{
swap(*this, other);
}
// assignment operator = (copy +) swap
C& C::operator=(C other) noexcept // C is taken by value to handle both copy and move
{
swap(*this, other);
return *this;
}
Note that, here, you need to provide a custom swap function instead of using std::swap, as mentioned by Howard Hinnant:
friend void swap(C& lhs, C& rhs) noexcept
{
// swap the members
}
If used properly, copy-and-swap incurs no overhead if the relevant functions are properly inlined (which should be pretty trivial). This idiom is very commonly used, and an average C++ programmer should have little trouble understanding it. Instead of being afraid that it will cause confusion, just take 2 minutes to learn it and then use it.
This time, we are swapping the values of the objects, and the lifetime of the object is not affected. The object is still the original object, just with a different value, not a brand new object. Think of it this way: you want to stop a kid from bullying others. Swapping values is like civilly educating them, whereas "destroying + constructing" is like killing them making them temporarily dead and giving them a brand new brain (possibly with the help of magic). The latter method can have some undesirable side effects, to say the least.
Like any other idiom, use it when appropriate — don't just use it for the sake of using it.
I believe the example in http://eel.is/c++draft/basic.life#8 clearly proves that assignment operators can be implemented through inplace "destroy + construct" assuming certain limitations related to non-const, non-overlapping objects and more.

Guaranteed copy elision in C++17 and emplace_back(...)

emplace_back(...) was introduced with C++11 to prevent the creation of temporary objects. Now with C++17 pure lvalues are even purer so that they do not lead to the creation of temporaries anymore (see this question for more). Now I still do not fully understand the consequences of these changes, do we still need emplace_back(...) or can we just go back and use push_back(...) again?
Both push_back and emplace_back member functions create a new object of its value_type T at some place of the pre-allocated buffer. This is accomplished by the vector's allocator, which, by default, uses the placement new mechanism for this construction (placement new is basically just a way of constructing an object at a specified place in memory).
However:
emplace_back perfect-forwards its arguments to the constructor of T, thus the constructor that is the best match for these arguments is selected.
push_back(T&&) internally uses the move constructor (if it exists and does not throw) to initialize the new element. This call of move constructor cannot be elided and is always used.
Consider the following situation:
std::vector<std::string> v;
v.push_back(std::string("hello"));
The std::string's move constructor is always called here that follows the converting constructor which creates a string object from a string literal. In this case:
v.emplace_back("hello");
there is no move constructor called and the vector's element is initialized by std::string's converting constructor directly.
This does not necessarily mean the push_back is less efficient. Compiler optimizations might eliminate all the additional instructions and finally both cases might produce the exact same assembly code. Just it's not guaranteed.
By the way, if push_back passed arguments by value — void push_back(T param); — then this would be a case for the application of copy elision. Namely, in:
v.push_back(std::string("hello"));
the parameter param would be constructed by a move-constructor from the temporary. This move-construction would be a candidate for copy elision. However, this approach would not at all change anything about the mandatory move-construction for vector's element inside push_back body.
You may see here: std::vector::push_back that this method requires either CopyInsertable or MoveInsertable, also it takes either const T& value or T&& value, so I dont see how elision could be of use here.
The new rules of mandatory copy ellision are of use in the following example:
struct Data {
Data() {}
Data(const Data&) = delete;
Data(Data&&) = delete;
};
Data create() {
return Data{}; // error before c++17
}
void foo(Data) {}
int main()
{
Data pf = create();
foo(Data{}); // error before c++17
}
so, you have a class which does not support copy/move operations. Why, because maybe its too expensive. Above example is a kind of a factory method which always works. With new rules you dont need to worry if compiler will actually use elision - even if your class supports copy/move.
I dont see the new rules will make push_back faster. emplace_back is still more efficient but not because of the copy ellision but because of the fact it creates object in place with forwarding arguments to it.

Noexcept and copy, move constructors

Everywhere I look it seems to be the agreement that the standard library must call copy constructors instead of move constructors when the move constructor is noexcept(false).
Now I do not understand why this is the case. And futher more Visual Studio VC v140 and gcc v 4.9.2 seems to do this differently.
I do not understand why noexcept this is a concern of e.g. vector. I mean how should vector::resize() be able to give strong exception guarantee if T does not. As I see it the exception level of vector will be dependend on T. Regardless if copy or move is used.
I understand noexcept to solely be a wink to the compiler to do exception handling optimization.
This little program calls the copy constructor when compiled with gcc and move constructor when compiled with Visual Studio.
include <iostream>
#include <vector>
struct foo {
foo() {}
// foo( const foo & ) noexcept { std::cout << "copy\n"; }
// foo( foo && ) noexcept { std::cout << "move\n"; }
foo( const foo & ) { std::cout << "copy\n"; }
foo( foo && ) { std::cout << "move\n"; }
~foo() noexcept {}
};
int main() {
std::vector< foo > v;
for ( int i = 0; i < 3; ++i ) v.emplace_back();
}
This is a multi-faceted question, so bear with me while I go through the various aspects.
The standard library expects all user types to always give the basic exception guarantee. This guarantee says that when an exception is thrown, the involved objects are still in a valid, if unknown, state, that no resources are leaked, that no fundamental language invariants are violated, and that no spooky action at a distance happened (that last one isn't part of the formal definition, but it is an implicit assumption actually made).
Consider a copy constructor for a class Foo:
Foo(const Foo& o);
If this constructor throws, the basic exception guarantee gives you the following knowledge:
No new object was created. If a constructor throws, the object is not created.
o was not modified. Its only involvement here is via a const reference, so it must not be modified. Other cases fall under the "spooky action at a distance" heading, or possibly "fundamental language invariant".
No resources were leaked, the program as a whole is still coherent.
In a move constructor:
Foo(Foo&& o);
the basic guarantee gives less assurance. o can be modified, because it is involved via a non-const reference, so it may be in any state.
Next, look at vector::resize. Its implementation will generally follow the same scheme:
void vector<T, A>::resize(std::size_t newSize) {
if (newSize == size()) return;
if (newSize < size()) makeSmaller(newSize);
else if (newSize <= capacity()) makeBiggerSimple(newSize);
else makeBiggerComplicated(newSize);
}
void vector<T, A>::makeBiggerComplicated(std::size_t newSize) {
auto newMemory = allocateNewMemory(newSize);
constructAdditionalElements(newMemory, size(), newSize);
transferExistingElements(newMemory);
replaceInternalBuffer(newMemory, newSize);
}
The key function here is transferExistingElements. If we only use copying, it has a simple guarantee: it cannot modify the source buffer. So if at any point an operation throws, we can just destroy the newly created objects (keep in mind that the standard library absolutely cannot work with throwing destructors), throw away the new buffer, and rethrow. The vector will look as if it had never been modified. This means we have the strong guarantee, even though the element's copy constructor only offers the weak guarantee.
But if we use moving instead, this doesn't work. Once one object is moved from, any subsequent exception means that the source buffer has changed. And because we have no guarantee that moving objects back doesn't throw too, we cannot even recover. Thus, in order to keep the strong guarantee, we have to demand that the move operation doesn't throw any exceptions. If we have that, we're fine. And that's why we have move_if_noexcept.
As to the difference between MSVC and GCC: MSVC only supports noexcept since version 14, and since that is still in development, I suspect the standard library hasn't been updated to take advantage yet.
The core issue is that it's impossible to offer strong exception safety with a throwing move constructor. Imagine if, in vector resize, half way through moving the elements to the new buffer, a move constructor throws. How could you possibly restore the previous state? You can't use the move constructor again because, well, that could just keep throwing.
Copying works for strong exception safety guarantee regardless of it's throwing nature because the original state is not damaged, so if you can't construct the whole new state, you can just clean up the partially-built state and then you're done, because the old state is still here waiting for you. Move constructors don't offer this safety net.
It's fundamentally impossible to offer strongly exception safe resize() with a throwing move, but easy with a throwing copy. This fundamental fact is reflected everywhere over the Standard library.
GCC and VS treat this differently because they are in different stages of conformance. VS has left noexcept to be one of the last features they implement, so their behaviour is a kind of mid-way between C++03's behaviour and C++11/14's. Particularly, since they don't have the ability to tell if your move constructor is actually noexcept or not, they basically just have to guess. From memory they simply assume that it is noexcept because throwing move constructors are not common and not being able to move would be a critical problem.

Strange behavior of copy-initialization, doesn't call the copy-constructor!

I was reading the difference between direct-initialization and copy-initialization (§8.5/12):
T x(a); //direct-initialization
T y = a; //copy-initialization
What I understand from reading about copy-initialization is that it needs accessible & non-explicit copy-constructor, or else the program wouldn't compile. I verified it by writing the following code:
struct A
{
int i;
A(int i) : i(i) { std::cout << " A(int i)" << std::endl; }
private:
A(const A &a) { std::cout << " A(const A &)" << std::endl; }
};
int main() {
A a = 10; //error - copy-ctor is private!
}
GCC gives an error (ideone) saying:
prog.cpp:8: error: ‘A::A(const A&)’ is private
So far everything is fine, reaffirming what Herb Sutter says,
Copy initialization means the object is initialized using the copy constructor, after first calling a user-defined conversion if necessary, and is equivalent to the form "T t = u;":
After that I made the copy-ctor accessible by commenting the private keyword. Now, naturally I would expect the following to get printed:
A(const A&)
But to my surprise, it prints this instead (ideone):
A(int i)
Why?
Alright, I understand that first a temporary object of type A is created out of 10 which is int type, by using A(int i), applying the conversion rule as its needed here (§8.5/14), and then it was supposed to call copy-ctor to initialize a. But it didn't. Why?
If an implementation is permitted to eliminate the need to call copy-constructor (§8.5/14), then why is it not accepting the code when the copy-constructor is declared private? After all, its not calling it. Its like a spoiled kid who first irritatingly asks for a specific toy, and when you give him one, the specific one, he throws it away, behind your back. :|
Could this behavior be dangerous? I mean, I might do some other useful thing in the copy-ctor, but if it doesn't call it, then does it not alter the behavior of the program?
Are you asking why the compiler does the access check? 12.8/14 in C++03:
A program is ill-formed if the copy
constructor or the copy assignment
operator for an object is implicitly
used and the special member function
is not accessible
When the implementation "omits the copy construction" (permitted by 12.8/15), I don't believe this means that the copy ctor is no longer "implicitly used", it just isn't executed.
Or are you asking why the standard says that? If copy elision were an exception to this rule about the access check, your program would be well-formed in implementations that successfully perform the elision, but ill-formed in implementations that don't.
I'm pretty sure the authors would consider this a Bad Thing. Certainly it's easier to write portable code this way -- the compiler tells you if you write code that attempts to copy a non-copyable object, even if the copy happens to be elided in your implementation. I suspect that it could also inconvenience implementers to figure out whether the optimization will be successful before checking access (or to defer the access check until after the optimization is attempted), although I have no idea whether that warranted consideration.
Could this behavior be dangerous? I
mean, I might do some other useful
thing in the copy-ctor, but if it
doesn't call it, then does it not
alter the behavior of the program?
Of course it could be dangerous - side-effects in copy constructors occur if and only if the object is actually copied, and you should design them accordingly: the standard says copies can be elided, so don't put code in a copy constructor unless you're happy for it to be elided under the conditions defined in 12.8/15:
MyObject(const MyObject &other) {
std::cout << "copy " << (void*)(&other) << " to " << (void*)this << "\n"; // OK
std::cout << "object returned from function\n"; // dangerous: if the copy is
// elided then an object will be returned but you won't see the message.
}
C++ explicitly allows several optimizations involving the copy constructor that actually change the semantics of the program. (This is in contrast with most optimizations, which do not affect the semantics of the program). In particular, there are several cases where the compiler is allowed to re-use an existing object, rather than copying one, if it knows that the existing object will become unreachable. This (copy construction) is one such case; another similar case is the "return value optimization" (RVO), where if you declare the variable that holds the return value of a function, then C++ can choose to allocate that on the frame of the caller, so that it doesn't need to copy it back to the caller when the function completes.
In general, in C++, you are playing with fire if you define a copy constructor that has side effects or does anything other than just copying.
In any compiler, syntax [and semantic] analysis process are done prior to the code optimization process.
The code must be syntactically valid otherwise it won't even compile. Its only in the later phase (i.e code optimization) that the compiler decides to elide the temporary that it creates.
So you need an accessible copy c-tor.
Here you can find this (with your comment ;)):
[the standard] also says that the temporary copy
can be elided, but the semantic
constraints (eg. accessibility) of the
copy constructor still have to be
checked.
RVO and NRVO, buddy. Perfectly good case of copy ellision.
This is an optimization by the compiler.
In evaluating: A a = 10; instead of:
first constructing a temporary object through A(int);
constructing a through the copy constructor and passing in the temporary;
the compiler will simply construct a using A(int).