What is the std :: function move constructor exception specification?

I looked at cppreference.com and they don't seem to point the noexcept specification to std::function(std::function&&) . It seems strange to me. Does the standard really give a guarantee nothrow in this case?

+7
source share
4 answers

I assume that a function object is capable of storing an arbitrary custom called object. When you move a function object that contains, the user object also moves, and there is no guarantee that this can be done without exception.

+3
source

Indication of the standard (at your request):

C ++ 11 Β§20.8.11.2.1 / 6 (from N3290):
function(function&& f);
template <class A> function(allocator_arg_t, const A& a, function&& f);
Effects: If !f , *this has no purpose; otherwise, move-constructs target f into target *this , leaving f in a valid state with an undefined value.

So sorry no noexcept in the move constructor.

There is a related defect report 2062 , which is still open, but it goes in a different direction, so to speak, namely that there is at least one noexcept , which, apparently, should not be there, regardless of its justification & hellip;

It may be that the goal is to support the calls that move constructors make. But this is only an assumption towards rationalization. For example, imagine a buffer redistribution in a vector of such function objects where an attempt is made to move the originals, and where in the middle somewhere one of them throws (I think it was an original example of Abraham and others), Oh, yes, they cannot be guaranteed to go back, and we too cannot go forward. Thus, the redistribution is not performed, and the operation that caused the redistribution fails, with the vector in an invalid state. The requirement of non-throwing movement of called objects could support such a general use of function objects, including optimized redistribution of vectors (etc.). Which IMHO makes doubt that the intention really was to make this compromise.

+5
source

None of the other answers really make sense to me. std::function has a default noexcept and a noexcept swap method, so a developer can always define a move constructor like this:

 function(function&& other) noexcept : function() { swap(*this, other); } 

Given the above, I'm not sure why the committee refused to make the move constructor noexcept . In any case, you can work around the problem by creating a shell to store std::function and move it using this technique.

+4
source

According to others, the std::function move constructor has NOT noexcept according to the C ++ standard.

As Peter Ruderman said, a developer can define a noexcept move noexcept using the swap method (which conforms to the noexcept standard).

And the GCC implementation actually uses this technique and defines the std::function move constructor as noexcept :

 /** * @brief %Function move constructor. * @param __x A %function object rvalue with identical call signature. * * The newly-created %function contains the target of @a __x * (if it has one). */ function(function&& __x) noexcept : _Function_base() { __x.swap(*this); } 

So, if you use GCC, you can assume that the std::function move constructor has noexcept value. But another implementation of the C ++ standard library may have a different implementation of the move constructor. Therefore, you should not rely on this.

It is better to have the default construct std::function ( noexcept ), and then use the swap method:

  // the line below could throw // std::function<void()> my_function(std::move(the_function_to_move_from)); // the code below is noexcept std::function<void()> my_function; my_function.swap(the_function_to_move_from); 

This is not as graceful as using the move constructor, but at least it is portable.

And be careful with the movement of semantics !!! This can do something that you do not expect, because it is usually indicated that the object is moving from a valid, but unspecified . For example:

 #include <iostream> #include <functional> struct A { void call() { if (fn) fn(); } std::function<void()> fn; }; int main() { std::function<void()> hello = []() { std::cerr << "Hello world" << std::endl; }; A a; hello = std::move(a.fn); a.call(); // may print "Hello world". Is it what you expect? return 0; } 

The GCC implementation does not print "Hello world", but another implementation may be implemented. The correct way is to explicitly clear the object moved from:

 hello = std::move(a.fn); a.fn = nullptr; 

But this makes moving semantically inconvenient to use (IMO).

0
source

All Articles