Прямое объявление, unique_ptr и инициализатор в классе

Я прочитал Требуется ли std::unique_ptr, чтобы узнать полное определение T? и переслать объявление с unique_ptr?, но мой вопрос более конкретен.

Следующие компиляции:

// 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;
};

Следующее не делает:

// 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};
};

Ошибка

$ 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’

РЕДАКТИРОВАТЬ: Насколько я понимаю, здесь происходит то, что инициализатор в классе подразумевает способность инициализировать unique_ptr<A> уже в момент объявления AUser, Так как тип unique_ptr<A> на самом деле unique_ptr<A, default_delete<A>>возможность инициализации подразумевает возможность инициализации default_delete<A>, И для этого A должен быть полностью определен.

Слабым звеном в этом рассуждении является предположение, что инициализатор в классе подразумевает возможность инициализации соответствующего члена данных в момент объявления класса! Это кажется интуитивно понятным, поскольку инициализатор является частью декларации. Но мне было бы удобнее, если бы я нашел что-то в стандарте, явно заявив об этом. В противном случае я все еще могу думать о решениях для реализации, которые бы не требовали этого. Например, компилятор может просто взять выражение инициализатора и применить его только в конструкторах, где инициализация атрибута не была явно задана.

Итак, может ли кто-нибудь направить меня к стандартному разделу / выдержке, который подразумевает необходимость полного определения A во втором случае? В стандарте я почти ничего не нашел об инициализаторах в классе (только нашел, что они упоминаются как "фигурные скобки-или-равно-инициализаторы нестатических элементов данных"), но ничего с этим не связано.

1 ответ

Во втором случае генерируется деструктор по умолчанию [неправильный] вместо AUser определение [/ неверное] (в этом случае это фактически делается после обработки всего кода). Так же, как определение конструктора внутри AUser сделал бы.

Тем не менее, в любом случае вам нужно дать определение A в том же модуле компиляции. Так может просто что-то подобное удовлетворит тебя?

#include <memory>

class A;

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


class A
{
  // ...
};


AUser::AUser() 
  : m_a(nullptr)
{ }
Другие вопросы по тегам