Reputation: 16847
Consider the following class, with a move constructor and move assignment operator:
class my_class
{
protected:
double *my_data;
uint64_t my_data_length;
}
my_class(my_class&& other) noexcept : my_data_length{other.my_data_length}, my_data{other.my_data}
{
// Steal the data
other.my_data = nullptr;
other.my_data_length = 0;
}
const my_class& operator=(my_class&& other) noexcept
{
// Steal the data
std::swap(my_data_length, other.my_data_length);
std::swap(my_data, other.my_data);
return *this;
}
What is the purpose of noexcept
here? I know that is hits to the compiler that no exceptions should be thrown by the following function, but how does this enable compiler optimizations?
Upvotes: 6
Views: 4947
Reputation: 171303
The special importance of noexcept
on move constructors and assignment operators is explained in detail in https://vimeo.com/channels/ndc2014/97337253
Basically, it doesn't enable "optimisations" in the traditional sense of allowing the compiler to generate better code. Instead it allows other types, such as containers in the library, to take a different code path when they can detect that moving the element types will never throw. That can enable taking an alternate code path that would not be safe if they could throw (e.g. because it would prevent the container from meeting exception-safety guarantees).
For example, when you do push_back(t)
on a vector, if the vector is full (size() == capacity()
) then it needs to allocate a new block of memory and copy all the existing elements into the new memory. If copying any of the elements throws an exception then the library just destroys all the elements it created in the new storage and deallocates the new memory, leaving the original vector is unchanged (thus meeting the strong exception-safety guarantee). It would be faster to move the existing elements to the new storage, but if moving could throw then any already-moved elements would have been altered already and meeting the strong guarantee would not be possible, so the library will only try to move them when it knows that can't throw, which it can only know if they are noexcept
.
Upvotes: 11
Reputation: 25613
First of all I would remark that in move constructors or move assignment nothing should throw and there seems to be no need to this ever. The only thing which must be done in constructors/assignment operator is dealing with already allocated memory and pointers to them. Normally you should not call any other methods which can throw and your own moving inside your constructor/operator has no need to do so. But on the other hand a simple output of a debug message breaks this rule.
Optimization can be done in a some different ways. Automatically by the compiler and also by different implementations of code which uses your constructors and assignment operator. Take a look to the STL, there are some specializations for code which are different if you use exceptions or not which are implemented via type traits.
The compiler itself can optimize better while having the guarantee that any code did never throw. The compiler have a guaranteed call tree through your code which can be better inlined, compile time calculated or what so ever. The minimum optimization which can be done is to not store all the informations about the actual stack frame which is needed to handle the throw condition, like deallocation variables on the stack and other things.
There was also a question here: noexcept, stack unwinding and performance
Maybe your question is a duplicate to that?
A maybe helpful question related to this I found here: Are move constructors required to be noexcept? This discuss the need of throwing in move operations.
What is the purpose of noexcept here?
At minimum saving some program space, which is not only relevant to move operations but for all functions. And if your class is used with STL containers or algorithms it can handled different which can result in better optimization if your STL implementation uses these informations. And maybe the compiler is able to get better general optimization because of a known call tree if all other things are compile time constant.
Upvotes: 0
Reputation: 1827
IMHO using noexcept
will not enable any compiler optimization on its own. There are traits in STL:
std::is_nothrow_move_constructible
std::is_nothrow_move_assignable
STL containters like vector
etc use these traits to test type T and use move constructors and assignment instead of copy constructors and assignment.
Why STL use these traits instead of:
std::is_move_constructible
std::is_move_assignable
Answer: to provide strong exception guarantee.
Upvotes: 1