5

I have read Is std::unique_ptr<T> required to know the full definition of T? and Forward declaration with unique_ptr?, but my question is more specific.

The following compiles:

// Compile with $ g++ -std=c++11 -c <filename>
#include <memory>
class A; // fwd declaration

class AUser
{
  AUser();  // defined elsewhere
  ~AUser(); // defined elsewhere
  std::unique_ptr<A> m_a;
};

The following doesn't:

// Compile with $ g++ -std=c++11 -c <filename>
#include <memory>
class A; // fwd declaration

class AUser
{
  AUser();  // defined elsewhere
  ~AUser(); // defined elsewhere
  std::unique_ptr<A> m_a{nullptr};
};

The error

$ g++ -std=c++11 -c fwd_decl_u_ptr.cpp 
In file included from /usr/include/c++/4.7/memory:86:0,
                 from fwd_decl_u_ptr.cpp:3:
/usr/include/c++/4.7/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = A]’:
/usr/include/c++/4.7/bits/unique_ptr.h:173:4:   required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = A; _Dp = std::default_delete<A>]’
fwd_decl_u_ptr.cpp:9:33:   required from here
/usr/include/c++/4.7/bits/unique_ptr.h:63:14: error: invalid application of ‘sizeof’ to incomplete type ‘A’

EDIT: As far as I understand, what is happening here is that the in-class initializer implies being capable of initializing unique_ptr<A> already at the moment of declaration of AUser. Since the type unique_ptr<A> is actually unique_ptr<A, default_delete<A>>, being able to initialize it implies being able to initialize default_delete<A>. And, for that, A has to be fully defined.

The weak link in this reasoning is the assumption that the in-class initializer implies capability of initializing the respective data member at the moment of declaration of the class! This seems an intuitive self-evidence, since the initializer is part of the declaration. But I would be more comfortable if I found something in the standard explicitly stating that. Otherwise I can still think of implementation solutions that wouldn't require it. For instance, the compiler could simply take the initializer expression and apply it only in constructors where the initialization of the attribute wasn't explicitly given.

So, can anyone refer me to a standard section/excerpt that implies the necessity for the full definition of A in the second case? I didn't find much about in-class initializers in the standard (only found them referred to as "brace-or-equal-initializers of non-static data members"), but nothing related to this.

Community
  • 1
  • 1
ricab
  • 2,697
  • 4
  • 23
  • 28

1 Answers1

0

The second case generate the default destructor [incorrect] in the place of AUser definition [/incorrect] (in this case, it is actually done after processing whole code). The same as a definition of a constructor inside AUser would do.

Still in any case you need to provide a definition of A in the same compilation unit. So maybe just something like this will satisfy you?

#include <memory>

class A;

class AUser
{
  std::unique_ptr<A> m_a;
  AUser();
};


class A
{
  // ...
};


AUser::AUser() 
  : m_a(nullptr)
{ }
Number47
  • 493
  • 4
  • 14
  • That's the point, I didn't define any constructor so why should the destructor be defined – ricab Sep 21 '13 at 13:35
  • Actually, adding a default value involves implicate extension of constructors (the value needs to be set when an object is created and a constructor is responsible for this, so some code need to be added to it). Thus in your second case you force redefinition of a default constructor and so the default destructor is also defined. -- This may be gcc specific, that it is done at this point, and maybe another compiler will generate the constructor and destructor just before first use of `AUser`. But any way, it needs to be done. – Number47 Sep 21 '13 at 14:36
  • Sorry your argument is a bit confusing. I don't get what you mean by "adding a default value involves implicate extension of constructors", nor by "you force redefinition of a default constructor", particularly when you then say "This may be gcc specific". – ricab Sep 23 '13 at 12:38
  • Using an in-class initializer does not equate to defining a ctor. In fact I could still define the default constructor and even overwrite `m_a`, and I could also define 10 other constructors that made use of the in-class initializer. There is nothing in your answer that proving the destructor of `AUser` has to be immediately generated in the second case. You're assuming things that may or may not make sense, but you're not showing why the compiler had to do it that way. – ricab Sep 23 '13 at 12:47
  • It didn't have to. As I said, it could wait till the first use of `AUser` object. It was choice of gcc, which was PROBABLY (I am not so deep into gcc to know such details) triggered, because you "touched" constructors. What you have done by adding the initializer is actually translated to additional entry `m_a{nullptr}` in initialization lists of all constructors except the copy one. Unless you specify an entry for `m_a` in a constructor by yourself. This makes, the default constructor had to be extended with this entry in your case. – Number47 Sep 23 '13 at 19:30
  • gcc, of course, did not need immediately define a default constructor and extend it. It could do this later. But probably, because you "touched" constructors by forcing this entry, it decided to do it in that place. -- You probably can complain about it at gcc bugzilla http://gcc.gnu.org/bugzilla/ . If they have some serious reasons to about that, they will probably answer you what it is. If not, maybe they will consider changing it. – Number47 Sep 23 '13 at 19:45
  • Sorry but reasoning with obvious "probably"s is not what I am looking for. By doing this, gcc is restricting the programs it accepts. It can only do that if that's what the standard says. What I would like to find is a definitive standard excerpt allowing or disallowing that. BTW, this is not only gcc, I checked with clang and it has the same behavior. – ricab Sep 24 '13 at 10:22
  • @ricab, but your code is incorrect due to the standard anyway. -- At least according the book I posses, as I do not have access to the standard, for which a band of lazy guys which done almost nothing for 20 years and maid C++, till C++11, more backward then C wants money from me. -- If you give a forward declaration of a class you are obligated to give the definition inside the same compilation unit. So you can't for example define `A` in another file which is not included to your fwd_decl_u_ptr.cpp, if fwd_decl_u_ptr.cpp the file you are going to compile. – Number47 Sep 26 '13 at 17:28
  • @ricab, in what case? Actually I have a feeling, you have a bad picture of, how to use forward class declarations. If you declare `class A;`, you indeed need to provide a definition of `A` somewhere in the same compilation unit. Particularly, if you want to compile `fwd_decl_u_ptr.cpp`, you need to provide it in `fwd_decl_u_ptr.cpp` or in a file included to it. – Number47 Sep 27 '13 at 11:46
  • fwd_decl_u_ptr.cpp was just for example purposes... in practice class AUser would be defined in a header file which, in turn, could be included elsewhere. This other code would have no business with the definition of A, which could have been compiled separately. It's pretty obvious you're not understanding the question and confusing stuff like "incorrect code due to the standard" and "C++ maids" don't add anything useful, so consider not loosing the opportunity to keep quiet when you don't know what you're saying, 'cause it's bugging me already – ricab Sep 27 '13 at 12:31
  • @ricab, I understand this, but I was trying to give you an example you can refer to. But well, I must say a person has to be quite an as*hole to treat this way someone giving his time to try to help. If you are aware of where the `A` definition needs to be put, then put it there and compile a proper file, and do not ask questions about compiling an incomplete piece of code. – Number47 Sep 27 '13 at 12:55
  • Look I understand you're *trying* to help, but you're not getting there. It's rather being me helping you understand the issue, which you still show in your last comment that you didn't understand so just forget it. I appreciate the effort anyway, but I would like to get someone else's opinion now – ricab Sep 27 '13 at 13:14