Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why this code won't compile if destructor is declared?

Tags:

c++

c++11

Consider the following C++11 code:

#include <thread>
#include <vector>

struct A {
    A() {}

    //virtual ~A() = default;
    //~A() = default;
    //~A() {};

    std::thread t;
};

int main()
{
    std::vector<A> v;
    v.emplace_back();
}

If any of the lines declaring the destructor on the previous code is uncommented, this code won't compile. The compiler complains about copy construtor of std::thread being deleted. But std::vector::emplace_back should make no use of copy constructor, so, why it fails? And why the heck the mention of the destructor matters?

GCC output (~A() {}; uncommented):

$ g++ --std=c++11 -o test test.cpp 
In file included from /usr/include/c++/4.8/memory:64:0,
                 from /usr/include/c++/4.8/thread:40,
                 from test.cpp:1:
/usr/include/c++/4.8/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = A; _Args = {A}]’:
/usr/include/c++/4.8/bits/stl_uninitialized.h:75:53:   required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<A*>; _ForwardIterator = A*; bool _TrivialValueTypes = false]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:117:41:   required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<A*>; _ForwardIterator = A*]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:258:63:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<A*>; _ForwardIterator = A*; _Tp = A]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:281:69:   required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = A*; _ForwardIterator = A*; _Allocator = std::allocator<A>]’
/usr/include/c++/4.8/bits/vector.tcc:415:43:   required from ‘void std::vector<_Tp, _Alloc>::_M_emplace_back_aux(_Args&& ...) [with _Args = {}; _Tp = A; _Alloc = std::allocator<A>]’
/usr/include/c++/4.8/bits/vector.tcc:101:54:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {}; _Tp = A; _Alloc = std::allocator<A>]’
test.cpp:17:17:   required from here
/usr/include/c++/4.8/bits/stl_construct.h:75:7: error: use of deleted function ‘A::A(const A&)’
     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
       ^
test.cpp:4:8: note: ‘A::A(const A&)’ is implicitly deleted because the default definition would be ill-formed:
 struct A {
        ^
test.cpp:4:8: error: use of deleted function ‘std::thread::thread(const std::thread&)’
In file included from test.cpp:1:0:
/usr/include/c++/4.8/thread:126:5: error: declared here
     thread(const thread&) = delete;
     ^

Clang output (~A() {}; uncommented):

$ clang++ --std=c++11 -o test test.cpp 
In file included from test.cpp:1:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/thread:40:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/memory:64:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_construct.h:75:38: error: 
      call to implicitly-deleted copy constructor of 'A'
    { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
                                     ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_uninitialized.h:75:8: note: 
      in instantiation of function template specialization
      'std::_Construct<A, A>' requested here
                std::_Construct(std::__addressof(*__cur), *__first);
                     ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_uninitialized.h:117:2: note: 
      in instantiation of function template specialization
      'std::__uninitialized_copy<false>::__uninit_copy<std::move_iterator<A *>,
      A *>' requested here
        __uninit_copy(__first, __last, __result);
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_uninitialized.h:258:19: note: 
      in instantiation of function template specialization
      'std::uninitialized_copy<std::move_iterator<A *>, A *>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_uninitialized.h:279:19: note: 
      in instantiation of function template specialization
      'std::__uninitialized_copy_a<std::move_iterator<A *>, A *, A>' requested
      here
      return std::__uninitialized_copy_a
                  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/vector.tcc:413:15: note: 
      in instantiation of function template specialization
      'std::__uninitialized_move_if_noexcept_a<A *, A *, std::allocator<A> >'
      requested here
              = std::__uninitialized_move_if_noexcept_a
                     ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/vector.tcc:101:4: note: 
      in instantiation of function template specialization 'std::vector<A,
      std::allocator<A> >::_M_emplace_back_aux<>' requested here
          _M_emplace_back_aux(std::forward<_Args>(__args)...);
          ^
test.cpp:17:4: note: in instantiation of function template specialization
      'std::vector<A, std::allocator<A> >::emplace_back<>' requested here
        v.emplace_back();
          ^
test.cpp:11:14: note: copy constructor of 'A' is implicitly deleted because
      field 't' has a deleted copy constructor
        std::thread t;
                    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/thread:126:5: note: 
      'thread' has been explicitly marked deleted here
    thread(const thread&) = delete;
    ^
1 error generated.
like image 504
lvella Avatar asked May 09 '14 23:05

lvella


1 Answers

(Answer taken from comments, no credit taken by me)

If a destructor is declared manually, the default move constructor is not created when compiling. Forcing the default move constructor to be used will fix this:

A(A&& o) = default;
like image 159
Karl Nicoll Avatar answered Oct 07 '22 03:10

Karl Nicoll