【问题标题】:Forward declaration, unique_ptr and in-class initializer前向声明、unique_ptr 和类内初始化器
【发布时间】:2013-09-25 17:25:13
【问题描述】:

我已阅读 Is std::unique_ptr<T> required to know the full definition of T?Forward declaration with 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’

编辑: 据我了解,这里发生的是类内初始化程序意味着能够在声明AUser 时已经初始化unique_ptr&lt;A&gt;。由于unique_ptr&lt;A&gt; 类型实际上是unique_ptr&lt;A, default_delete&lt;A&gt;&gt;,因此能够初始化它意味着能够初始化default_delete&lt;A&gt;。为此,A 必须完全定义。

这个推理中的薄弱环节是假设类内初始化器意味着在类声明时初始化相应数据成员的能力!这似乎是一个直观的不言而喻,因为初始化程序是声明的一部分。但是,如果我在标准中找到明确说明的内容,我会更舒服。否则我仍然可以想到不需要它的实施解决方案。例如,编译器可以简单地采用初始化表达式并将其仅应用于未明确给出属性初始化的构造函数。

那么,任何人都可以向我推荐一个标准部分/摘录,暗示在第二种情况下 A 的完整定义是必要的吗?我在标准中没有找到太多关于类内初始化器的信息(只发现它们被称为“brace-or-equal-initializers of non-static 数据成员”),但与此无关。

【问题讨论】:

  • 第二种情况的错误是什么?
  • 另外:ideone.com/p2iPsv - 似乎第一个也会导致问题。
  • @KirilKirov 它会导致问题,因为您还没有定义 A 类...
  • @KirilKirov:如果您尝试在没有定义A 的情况下生成可执行文件,您显然会遇到问题。但是,如果您只编译片段(当然假设A 的定义将在其他地方提供),您不会。我将使用错误更新原始帖子。
  • @Everybody - 对,对,我的错 :)

标签: c++ forward-declaration unique-ptr in-class-initialization


【解决方案1】:

第二种情况生成默认析构函数[不正确] 代替AUser定义 [/不正确](在这种情况下,它实际上是在处理完整个代码之后完成的)。就像在 AUser 中定义构造函数一样。

在任何情况下,您都需要在同一编译单元中提供A 的定义。所以也许这样的事情会让你满意?

#include <memory>

class A;

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


class A
{
  // ...
};


AUser::AUser() 
  : m_a(nullptr)
{ }

【讨论】:

  • 重点,我没有定义任何构造函数,为什么要定义析构函数
  • 其实添加默认值涉及到构造函数的隐含扩展(该值需要在创建对象时设置,并且由构造函数负责,所以需要添加一些代码)。因此,在第二种情况下,您强制重新定义默认构造函数,因此也定义了默认析构函数。 -- 这可能是 gcc 特定的,它在此时完成,并且可能另一个编译器将在第一次使用 AUser 之前生成构造函数和析构函数。但无论如何,它需要完成。
  • 抱歉你的论点有点混乱。我不明白你所说的“添加默认值涉及构造函数的隐含扩展”,也不是“你强制重新定义默认构造函数”的意思,特别是当你说“这可能是 gcc 特定的”时。
  • 使用类内初始化器并不等同于定义一个ctor。事实上,我仍然可以定义默认构造函数,甚至覆盖m_a,我还可以定义其他 10 个使用类内初始化程序的构造函数。您的答案中没有任何内容证明必须在第二种情况下立即生成 AUser 的析构函数。你假设的事情可能有意义也可能没有意义,但你没有说明为什么编译器必须这样做。
  • 没必要。正如我所说,它可以等到第一次使用AUser 对象。这是 gcc 的选择,它可能是触发的(我对 gcc 的了解并不深入,无法了解这些细节),因为您“触及”了构造函数。您通过添加初始化程序所做的实际上转换为除副本之外的所有构造函数的初始化列表中的附加条目m_a{nullptr}。除非您自己在构造函数中为 m_a 指定条目。这使得,在您的情况下,必须使用此条目扩展默认构造函数。
猜你喜欢
  • 2015-08-29
  • 1970-01-01
  • 2022-01-10
  • 1970-01-01
  • 2023-02-21
  • 1970-01-01
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
相关资源
最近更新 更多