Copy-initialization of a simple RAII wrapper with lambda unexpectedly terminates with GCC and Clang

I ran into an unexpected problem while creating a trivial RAII wrapper.

Not to mention the logical incompleteness of the code below (the constructor operator and the assignment operator are not deleted, etc., this means that it is SSCCE), which amazes me is that copy-initialization of my wrapper with a temporary lambda leads to compilation error, while direct initialization does not work.

This behavior can be observed on both GCC 4.7.2 and Clang 3.2, while ICC 13.0.1 and VC10 will compile both versions without problems.

#include <iostream> #include <functional> using namespace std; struct A { template<typename F> A(F&& f) : _f(forward<F>(f)) { } ~A() { _f(); } private: std::function<void()> _f; }; int main() { // A a = [] () { cout << "Hello" << endl; }; // ERROR! A a([] () { cout << "Hello" << endl; }); // OK } 

Who is right , and what is the problem with those who are wrong? Is this a problem with the implementation of the standard C ++ library or, rather, with the compiler?

References to the C ++ 11 standard are welcome.

EDIT:

Here is the error created by Clang 3.2:

 Compilation finished with errors: In file included from source.cpp:2: /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/functional:1925:2: error: type 'A' does not provide a call operator (*_Base::_M_get_pointer(__functor))( ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/functional:2297:33: note: in instantiation of member function 'std::_Function_handler<void (), A>::_M_invoke' requested here _M_invoker = &_My_handler::_M_invoke; ^ source.cpp:9:16: note: in instantiation of function template specialization 'std::function<void ()>::function<A>' requested here A(F&& f) : _f(forward<F>(f)) { } ^ source.cpp:20:7: note: in instantiation of function template specialization 'A::A<A>' requested here A a = [] () { cout << "Hello" << endl; }; // ERROR! ^ 

1 error generated.

+4
source share
1 answer

The error message (gcc 4.7.2) is quite informative:

 c++/4.7/functional: In instantiation of 'static void std::_Function_handler<void(_ArgTypes ...), _Functor>::_M_invoke(const std::_Any_data&, _ArgTypes ...) [with _Functor = A; _ArgTypes = {}]': c++/4.7/functional:2298:6: required from 'std::function<_Res(_ArgTypes ...)>::function(_Functor, typename std::enable_if<(! std::is_integral<_Functor>::value), std::function<_Res(_ArgTypes ...)>::_Useless>::type) [with _Functor = A; _Res = void; _ArgTypes = {}; typename std::enable_if<(! std::is_integral<_Functor>::value), std::function<_Res(_ArgTypes ...)>::_Useless>::type = std::function<void()>::_Useless]' source.cpp:9:32: required from 'A::A(F&&) [with F = A]' source.cpp:22:44: required from here c++/4.7/functional:1926:2: error: no match for call to '(A) ()' 

The problem is that your class does not have an implicit move constructor available for use in copy-initialization. An implicitly defined move constructor is deleted because A has a user-defined destructor (12.8p9b4).

Add

 A(A &&) = default; 

Note: since the selected move constructor is selected by default, the destructor must verify that _f not empty; since the std::function move constructor does not guarantee that the target is left empty, you must also make this change yourself:

 A(A &&a): _f() { std::swap(_f, a._f); } ~A() { if (_f) _f(); } 

Recall that (at 8.5p17) copy initialization involves creating a temporary prvalue value, which is then used to directly initialize the target. The choice between a template constructor and an implicitly defined copy constructor; a template constructor with a template argument of type A is preferred because A && binds to prvalue A better than const A & does.

An alternative (perhaps better) is to disable the template constructor for arguments A :

 template<typename F, typename = typename std::enable_if<!std::is_same<F, A>::value>::type> A(F&& f) : _f(forward<F>(f)) { } 

In this case, an implicit copy constructor will be selected, so the destructor does not need to check the state of _f ; however, if the compiler does not copy, then it (and _f ) will be called twice.

Allowed copying (12.8p31); (12.8p32), but as far as I can tell (and inaction), the compiler should not check that it compiles. Therefore, the compiler is allowed to compile or refuse to compile the program; if compiled, it must execute a copy.

+3
source

All Articles