4

I just noticing a std::vector<Foo> of mine was copying instead of moving its elements when resizing - even though Foo has a move ctor:

class Foo {
    // ...
    Foo(Foo&& other) : id_(other.id_), ptr_(other.ptr_), flag(other.flag)
    {
        other.flag = false;
    };
    // ...
    int   id_; 
    void* ptr_; 
    bool  flag;
}

Then I read:

Resize on std::vector does not call move constructor

which reminded me that std::vector will only use move construction if the elements' move ctor is declared noexcept. When I add noexcept, move ctor's are called.

My question is: Why, given the move ctor's code, does the compiler not determine it to be noexcept? I mean, it can know for a fact that an exception cannot be thrown. Also, is inferring noexcept disallowed by the standard, or not just done by my specific compiler?

I'm using GCC 5.4.0 on GNU/Linux.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 3
    There is an explicit list here saying when the compiler can assume `noexcept` http://en.cppreference.com/w/cpp/language/noexcept_spec The standard needs to play-safe to ensure the strong exception guarentee. Analysing user-written code is not safe enough. – Richard Critten Jun 06 '18 at 16:34
  • @RichardCritten: So you're saying the compiler is supposed to _assume_ my code may throw, even though it can easily tell that it can't (using the potentially-throwing-expressions logic)? – einpoklum Jun 06 '18 at 16:41
  • I'm saying that may be the reasoning behind the wording of the standard. – Richard Critten Jun 06 '18 at 16:42
  • One reason I suppose is that you can technically do many different things in a custom defined move constructor (although many of those might not be a good practice) that might actually result in an exception being thrown (in the fact, AFAIK nothing will prevent you to throw an exception from your move constructor explicitly). – EmDroid Jun 06 '18 at 16:42
  • 1
    @einpoklum if the move constructor was in a library for example, how would the compiler be able to assume that it will not throw? All it would be able to see is the declaration in the header. – Jesper Juhl Jun 06 '18 at 16:46
  • 2
    @axalis: I can, but I'm not, and the compiler can know / does know that I'm not.... of course, if the compiler doesn't know, then it has to assume there might be an exception, that's obvious. – einpoklum Jun 06 '18 at 16:46
  • @NathanOliver: You won't get a compiler error from std::vector copying instead of moving, you'll get very subtle bugs at run-time. – einpoklum Jun 06 '18 at 17:32

1 Answers1

5

tl;dr: The compiler is not allowed to infer noexcept

Why, given the move ctor's code, does the compiler not determine it to be noexcept?

Because noexcept specification is determined based on the declaration - not the definition. This is analogous to how const specification works. The compiler isn't allowed to determine a function to be const even though its implementation doesn't modify any members.

is inferring noexcept disallowed by the standard

As I understand, yes:

[except.spec] ... absence of an exception-specification in a function declarator other than that for a destructor (12.4) or a deallocation function (3.7.4.2) denotes an exception specification that is the set of all types.

Inferring something other than set of all types would contradict this rule. Of course, when the compiler can prove that no exception can be thrown, it can optimise away any stack unwinding code under the as-if rule, but such optimisations cannot affect SFINAE introspection.


There has been discussion about possibility of introducing noexcept(auto), which would be an explicit way of letting the compiler infer noexcept specification.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • I like your answer, I don't like the situation with the standard. – einpoklum Jun 06 '18 at 17:33
  • @einpoklum would you like const specifier to be inferred as well? noexcept specification is a promise to the user of the function that the author of the API might not wish to be required to be accountable for implicitly. – eerorika Jun 06 '18 at 17:39
  • Actually, it's not exactly a promise to the user. I mean, you can throw exceptions just fine in a `noexcept` function, you'll just terminate the program instead of finding an exception handler, right? – einpoklum Jun 06 '18 at 17:42
  • But I will say that I want that whenever we ask "do we have a method/function so-and-so which is `noexcept`?", and there's a method not marked this way or that, which the compiler knows to be `noexcept` - then the answer would be "yes". – einpoklum Jun 06 '18 at 17:43
  • @einpoklum it's a promise that the user doesn't need to handle any exceptions, because no exception will propagate out of the function. Sure, it doesn't promise that the function won't cause the process to terminate, but that's besides the point. – eerorika Jun 06 '18 at 18:04