Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::list<std::unique_ptr>: empty initializer list vs. default constructor

The code

#include <list>
#include <memory>

class B;
class A {
    std::list<std::unique_ptr<B>> bs;
public:
    A();
    ~A();
};

int main()
{
    A x;
    return 0;
}

obviously compiles. It doesn't link because A::A() and A::~A() are missing, but that is expected and alright. Changing

std::list<std::unique_ptr<B>> bs;

which is supposed to call the std::list's standard constructor

list() : list(Allocator()) {}

(C++14 and up) to

std::list<std::unique_ptr<B>> bs{};

which is supposed to call list(std::initializer_list, const Allocator & = Allocator()); the default constructor too. (Thanks to Nicol Bolas, who rightly referred to [over.match.list] 13.3.1.7) gives the following error with c++ (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010 and the --std=c++17 parameter:

/usr/include/c++/5/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = B]’:
/usr/include/c++/5/bits/unique_ptr.h:236:17:   required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = B; _Dp = std::default_delete<B>]’
/usr/include/c++/5/bits/stl_list.h:106:12:   required from ‘void __gnu_cxx::new_allocator<_Tp>::destroy(_Up*) [with _Up = std::_List_node<std::unique_ptr<B> >; _Tp = std::_List_node<std::unique_ptr<B> >]’
/usr/include/c++/5/bits/list.tcc:75:4:   required from ‘void std::__cxx11::_List_base<_Tp, _Alloc>::_M_clear() [with _Tp = std::unique_ptr<B>; _Alloc = std::allocator<std::unique_ptr<B> >]’
/usr/include/c++/5/bits/stl_list.h:446:17:   required from ‘std::__cxx11::_List_base<_Tp, _Alloc>::~_List_base() [with _Tp = std::unique_ptr<B>; _Alloc = std::allocator<std::unique_ptr<B> >]’
/usr/include/c++/5/bits/stl_list.h:507:11:   required from here
/usr/include/c++/5/bits/unique_ptr.h:74:22: error: invalid application of ‘sizeof’ to incomplete type ‘B’
  static_assert(sizeof(_Tp)>0,
                      ^

Which barks about the type of B being incomplete. My question is:

Why does the initializer_list constructor need the complete type of B for an empty initializer list?

Pointers to the relevant parts of the standard are always appreciated.

like image 899
apriori Avatar asked Jan 29 '16 17:01

apriori


1 Answers

I think you've stepped onto the bleeding edge.

This appears to be an active issue on the CWG (Core Working Group on the C++ committee).

CWG 1396 appears to be concerned with this very issue. This issue links to CWG 1360 which says in part:

The problem is exacerbated with class templates, since the current direction of CWG is to instantiate member initializers only when they are needed (see issue 1396).

In your example, the initializer of bs is never needed, and thus by the "direction" referred to above, should never be instantiated. We just aren't there yet today. Both of these issues have status drafting, meaning: they're working on it.

FWIW, VS-2015 as reported at http://webcompiler.cloudapp.net compiles (but of course does not link) this example.

like image 155
Howard Hinnant Avatar answered Oct 20 '22 02:10

Howard Hinnant