Are compiler assignment statements unsafe?

I realized that the C ++ compiler generates assignment operators as follows:

struct X { std::vector<int> member1; std::vector<int> member2; X& operator=(const X& other) { member1 = other.member1; member2 = other.member2; } }; 

Is this exception unsafe? If member2 = other.member2 thrown away, the original side effects of the assignment are not canceled.

+6
source share
2 answers

Using security system 4 security levels :

  • No throw
  • Strong guarantee - the operation is completed or completely rolled back
  • The main guarantee is that invariants are preserved, there are no resource leaks.
  • No guarantee

The assignment operator generated by the compiler has a basic exception guarantee if each member of the object provides a basic exception guarantee or "better", and if the invariants of the objects have no dependencies between the members.

This has a no-throw guarantee if each member nomination also does not have a throw guarantee.

This rarely (if ever) has a strong guarantee, which you might call "exception unsafe."

Copy-to-copy idiom is popular because the no-throw swap entry is often lightweight, and designers should already provide a reliable exception guarantee. The result is operator= with a robust exception guarantee. (In the case of move it is pseudo-strong, since the input is often not a rollback, but it is an rvalue)

 void swap( Foo& other ) noexcept; // implement this Foo& operator=( Foo const& f ) { Foo tmp(f); swap( tmp ); return *this; } Foo& operator=( Foo && f ) { Foo tmp(std::move(f)); swap( tmp ); return *this; } 

If you also take a copy by value, you can sometimes update operator= to what you don't throw away (with one exception, perhaps in the argument construct).

 Foo& operator=( Foo f ) noexcept { swap( f ); return *this; } 

in some cases, some Foo constructors are noexcept , while others are not: by accepting a different byte value, we provide the best exception guarantee that we can end up with (since the argument = can sometimes be a direct build, either by elite, or a direct build through {} ) .

For a language (at least at this time) it is not practical to implement “copy swap” operator= with a strong guarantee for several reasons. First, C ++ works on “you pay only for what you use,” and “copying” can be more expensive than copying in order. Secondly, swap is not currently part of the main language (there are suggestions to add operator :=: and collapse it). Third, backward compatibility with previous versions of C ++ and C.

+8
source

Edited June 4, 2014

My initial answer was based on my understanding that the original poster was looking for an assignment operator guaranteed not to throw an exception. According to various commentators, it has become apparent that exceptions are OK as long as the object remains unchanged with the exception.

The suggested way to do this is with temporary variables and std :: swap ().

 X& X::operator=(const X& other) { // assign to temps. If this throws, the object // has not changed. auto m1 = other.member1; auto m2 = other.member2; // the theory is, that swap won't throw // can we rely on that? std::swap(m1, member1); std::swap(m2, member2); return *this; } 

In fact, we cannot assume that swap () will not throw an exception unless we know a little about the components of our object.

To be sure that swap () will never be thrown, we need member objects that will be Move Assignable and Move construction .

In the above example with a C ++ 11 compiler or later, std :: vector <int> is moveable and move constructively, so we are safe. However, as a general solution, we always need to know any assumptions we make and check what they hold.

+1
source

Source: https://habr.com/ru/post/970247/


All Articles