6

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?

FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225

3 Answers3

11

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.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • Why it can NOT perform better optimizations? My understanding is that better inlining can be performed. Is this wrong and if so why? – Klaus Aug 26 '15 at 11:38
  • @Klaus, for the OP's examples the compiler can already see that those operations don't throw, adding `noexcept` tells it nothing. In general, if the function definition is visible and suitable for inlining then the compiler has all the information it needs to make that decision anyway, without `noexcept`. Any advantages in codegen (such as not needing to generate unwinding info) applies to _all_ functions, whereas the question asks why `noexcept` is important in the context of move constructors and assignment operators. – Jonathan Wakely Aug 26 '15 at 11:41
  • Of course, if all is visible in the translation unit, everything is quite perfect. This is also true for `const` and `final`. My understanding is, that if the definitions are NOT visible, the optimzation can be done better if the declaration gives some "hints" like `noexcept` the optimization becomes a bit better. Not as perfect as with the code in the same translation unit but better as have neither `noexcept` and the code itself. – Klaus Aug 26 '15 at 11:45
  • That still isn't specific to move operations though, that would apply equally to all functions. – Jonathan Wakely Aug 26 '15 at 11:46
  • Thats also clear! Specific to move operations is only the aspect that STL uses these information while having specializations for the using code. But I am also interested if having noexcept can help to generate better code if the definition is not visible. – Klaus Aug 26 '15 at 11:47
1

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.

Elohim Meth
  • 1,777
  • 9
  • 13
  • Ah, so it is important for me to always remember to add `noexcept` to a move constructor or move assignment operator? – FreelanceConsultant Aug 26 '15 at 10:45
  • 2
    Yes, this is important if your copy constructor performs deep copy of data for example and you want to use your class in STL containers. – Elohim Meth Aug 26 '15 at 10:46
0

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.

Community
  • 1
  • 1
Klaus
  • 24,205
  • 7
  • 58
  • 113
  • Yeah this doesn't answer my question... From what you say in the first paragraph, it sounds like `noexcept` is just used by the compiler to check whether any part of the function can throw an exception, which if true would raise a compiler error? (Analagous to the use of `const` at the end of a member function declaration? In this case, we say "this function will not modify any member data" and if it does, then we get a compiler error.) Is this the correct interpretation of your first paragraph? – FreelanceConsultant Aug 26 '15 at 11:14
  • You will not get a compile error at all but if a throw occurs in runtime, your program normally ends. So the compiler have an idea what the call tree looks like. And if a throw occurs, it doesn't matter, because the execution of this method is never continued because the program ends. But my first paragraph only asks why anybody should do throwing in a move operation. I expect that there is no need for that at all. – Klaus Aug 26 '15 at 11:16
  • Ok, so basically my interpretation is incorrect and it is important to put `noexcept` there? – FreelanceConsultant Aug 26 '15 at 11:21
  • I added my answer in hope it becomes a bit clearer. Yes, noexcept will result typically in better code generation with less space consumption. – Klaus Aug 26 '15 at 11:24
  • @user3728501, _"which if true would raise a compiler error?"_ no, as Klaus says, exception specifications are not checked at compile-time. – Jonathan Wakely Aug 26 '15 at 11:40
  • @JonathanWakely Thanks for clarifying – FreelanceConsultant Aug 26 '15 at 11:51