10

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.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
apriori
  • 1,260
  • 11
  • 29
  • 1
    In case of exception you might have to destroy B, so its definition is required. – Jarod42 Jan 29 '16 at 18:13
  • In other words, the destructor must also be fully defined, and for this the `unique_ptr` needs a full type, see http://stackoverflow.com/questions/9954518/stdunique-ptr-with-an-incomplete-type-wont-compile @Jarod42 You should post an answer, as this is a good question and not so obvious. – vsoftco Jan 29 '16 at 18:15
  • 3
    FYI: `std::list> bs{};` will call the *default* constructor. In accord with 13.3.1.7 of the C++14 standard, if an empty braced-init-list is passed to a type that has a default constructor, then that is what will be called. – Nicol Bolas Jan 29 '16 at 18:57
  • `std::list> bs;` should call the default constructor too, right? Why doesn't the one with the `{}` work, then? – apriori Jan 29 '16 at 19:40
  • @apriori As a member declaration, `std::list> bs;` doesn't call any constructor. – T.C. Jan 29 '16 at 19:41
  • @T.C. It is a non-static data member definition, see [basic.def] 3.1 (2). [class.init] 12.6 (1) says: "When no initializer is specified for an object of (possibly cv-qualified) class type (or array thereof), or the initializer has the form (), the object is initialized as specified in 8.5.", [decl.init] 8.5 (12) says "If no initializer is specified for an object, the object is default-initialized". [dcl.init] 8.5 (7.1) goes on: if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called [...]" – apriori Jan 29 '16 at 20:16
  • By your logic, you can't have any non-static data member of a type without a default constructor, which is plainly nonsense. – T.C. Jan 29 '16 at 20:18
  • @T.C. I quote the standard with "[...] an object of (possibly cv-qualified) class type (or array thereof) [...]". Do you have an example of a non-static data member of class type without a default constructor? `struct X { X() = delete; }; class A { X x; }; int main() { A a; return 0; }` certainly doesn't work. – apriori Jan 29 '16 at 22:17
  • You really need help to think up an example??? `struct X <% X(int); %>; class A <% X x; public: A() : x(0) <%%> %> a; ` I think you should be learning the language first, before trying to language-lawyer it. – T.C. Jan 29 '16 at 22:21
  • Now you're ignoring the "When no initializer is specified [...]" part. I thought it would be clear from the context when I wrote my follow-up comment. Thanks for participating, but with that rude attitude, I'd rather not keep on discussing with you. – apriori Jan 29 '16 at 22:38
  • And "no initializer is specified" in `X x;`, either. The only difference between this and your code is that I gave the constructor a definition and you didn't. – T.C. Jan 29 '16 at 22:55
  • @T.C. Not in `X x;`, but after the constructor in a `ctor-initializer`. [class.base.init] 12.6.2 (1): "In the definition of a constructor for a class, **initializers** for direct and virtual base subobjects and **non-static data members** can be specified by a ctor-initializer". That is what you did, and that is why no *default* constructor was needed. – apriori Jan 29 '16 at 23:19
  • @apriori Correct, and not really relevant to my point. The point I'm trying to make is that `struct A { X x; A(); } a;` - which is your case - doesn't call `X`'s default constructor (or require that it has one). The constructor called for `x` entirely depends on how `A::A()` is written. – T.C. Jan 29 '16 at 23:27
  • @T.C. Right, that is why I wrote "which is supposed to call the `std::list`'s standard constructor" in my question, so I assumed a definition of `A::A() = default`. Without that assumption, your comment "`std::list> bs;` doesn't call any constructor" could be interpreted as something along the lines of "`std::list> bs;` doesn't call any *specific* constructor *, because the constructor called can be determined by an optional `ctor-initializer` specified in the ctor's implementation.*", which is IMO correct. – apriori Jan 29 '16 at 23:39
  • @apriori Well, it's not defaulted in your question (and in fact, if you did default it, you'd see the same errors in both cases!) – T.C. Jan 29 '16 at 23:42

1 Answers1

2

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.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Thank you very much Howard, that was exactly what I was looking for. Seems like this issue is unresolved for some time now. – apriori Jan 29 '16 at 22:44
  • CWG1396 is about class templates. `A` in OP's example is not a template. Default function arguments, for example, are not instantiated until needed for function templates, but do get checked for functions. `void f(double = "");` is ill-formed even if you never call it in a way that requires using the default argument. – T.C. Jan 29 '16 at 23:30
  • 1
    @T.C.: Perhaps. I do not regularly sit in on the CWG meetings, and so am not intimately familiar with the issue. I'm triggering on the title of CWG1396: "Deferred instantiation and checking of non-static data member initializers", which could be interpreted to pertain to the OP's code. I welcome other theories, or insight from those who are better connected to the CWG than I am. – Howard Hinnant Jan 29 '16 at 23:45