Providing a (empty) user-defined destructor causes a compilation error

Code that compiles fine (on GCC 4.7.2), when I don't have a user-defined destructor, throws errors when even an empty user destructor is provided:

#include <memory> class Test { std::unique_ptr<int> val; }; template <typename Type> class B { public: //destructor: // if I comment this out, the code compiles just fine: ~B() { } private: Test a; }; int main() { auto s = B<int>(); } 

The distinctive points of the error that occurs when the destructor is not commented out are:

  • The copy constructor for the test does not exist and was not implicitly created because it would be poorly formed
  • Something tried to use the remote copy constructor for unique_ptr.

The full error output for anyone interested is at the bottom of this post.

I know that unique_ptr cannot be copied (unless the argument is rvalue), and therefore the compiler cannot generate the correct implicit copy constructor for the Test class.

What I can’t understand is that in order to determine even for an empty destructor it is necessary to suddenly require these copy tools. Obviously, when things like unique_ptr , this cannot be provided.

Can anyone here enlighten me why this is so?

Full error output when the destructor is not commented out:

 In file included from /usr/include/c++/4.7/list:64:0, from ../../Dropbox/Programming/C++/test/main.cpp:2: /usr/include/c++/4.7/bits/stl_list.h: In instantiation of 'std::_List_node<_Tp>::_List_node(_Args&& ...) [with _Args = {const Test&}; _Tp = Test]': /usr/include/c++/4.7/ext/new_allocator.h:110:4: required from 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::_List_node<Test>; _Args = {const Test&}; _Tp = std::_List_node<Test>]' /usr/include/c++/4.7/bits/stl_list.h:503:8: required from 'std::list<_Tp, _Alloc>::_Node* std::list<_Tp, _Alloc>::_M_create_node(_Args&& ...) [with _Args = {const Test&}; _Tp = Test; _Alloc = std::allocator<Test>; std::list<_Tp, _Alloc>::_Node = std::_List_node<Test>]' /usr/include/c++/4.7/bits/stl_list.h:1533:63: required from 'void std::list<_Tp, _Alloc>::_M_insert(std::list<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const Test&}; _Tp = Test; _Alloc = std::allocator<Test>; std::list<_Tp, _Alloc>::iterator = std::_List_iterator<Test>]' /usr/include/c++/4.7/bits/stl_list.h:997:9: required from 'void std::list<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = Test; _Alloc = std::allocator<Test>; std::list<_Tp, _Alloc>::value_type = Test]' /usr/include/c++/4.7/bits/stl_list.h:1466:6: required from 'void std::list<_Tp, _Alloc>::_M_initialize_dispatch(_InputIterator, _InputIterator, std::__false_type) [with _InputIterator = std::_List_const_iterator<Test>; _Tp = Test; _Alloc = std::allocator<Test>]' /usr/include/c++/4.7/bits/stl_list.h:582:9: required from 'std::list<_Tp, _Alloc>::list(const std::list<_Tp, _Alloc>&) [with _Tp = Test; _Alloc = std::allocator<Test>; std::list<_Tp, _Alloc> = std::list<Test>]' ../../Dropbox/Programming/C++/test/main.cpp:11:7: required from here /usr/include/c++/4.7/bits/stl_list.h:115:71: error: use of deleted function 'Test::Test(const Test&)' ../../Dropbox/Programming/C++/test/main.cpp:5:7: note: 'Test::Test(const Test&)' is implicitly deleted because the default definition would be ill-formed: ../../Dropbox/Programming/C++/test/main.cpp:5:7: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>; std::unique_ptr<_Tp, _Dp> = std::unique_ptr<int>]' In file included from /usr/include/c++/4.7/memory:86:0, from ../../Dropbox/Programming/C++/test/main.cpp:3: /usr/include/c++/4.7/bits/unique_ptr.h:262:7: error: declared here 
+6
source share
2 answers

If you define ~B() , this suppresses the B constructor, so the compiler tries to generate a copy constructor, but it does not work because unique_ptr not constructive.

If you omit ~B() , the B constructor is created and used in main .

You can request an automatically created movement mechanism:

 B(B &&) = default; 

This is a standard feature for backward compatibility with C ++ 03 code; in accordance with Rule Three, a code that writes its own destructor (etc.) is considered to be the manager of its own resources, so automatic creation of a move constructor will not be suitable unless explicitly requested.

+13
source

Well, you said it yourself; when you provide a user-defined destructor, you deny the compiler's ability to generate things like an implicit move mechanism. The contents of your user-defined destructor (whether empty or otherwise) are completely irrelevant.

[C++11: 12.7/9]: If the definition of class X does not explicitly declare the move constructor, it will be declared implicitly as default, if and only if :

  • X does not have a user-declared copy constructor,
  • X does not have a user-declared copy destination operator,
  • X does not have a user-declared move destination operator,
  • X does not have a user-declared destructor and
  • the move constructor will not be implicitly defined as remote.

[Note: If the move constructor is implicitly declared or explicitly specified, expressions that otherwise call the move constructor may instead refer to the copy constructor. -end note]

The text of the note indicates the exact case that you see, which leads to compilation failure, since unique_ptr cannot be copied; although you are not copying it, and although copy / move might not be necessary to initialize s , the operation should still be available (for [C++11: 12.8/31-32] ).

+3
source

Source: https://habr.com/ru/post/943536/


All Articles