class 之前或 Pimple 习语中的成员之前的 C++ 前向声明?

C++ Forward declaration before class or before member in the Pimple idiom?

大多数 Pimpl 示例如下所示:

更新: 两种情况都失败了,即有和没有命名空间。请参阅 R Sahu 在 的回答。 class Impl 必须使用 class 名称限定 Simpl

// Simple.h
#include <memory>
class Simple {
     struct Impl; // WORKS!
     std::unique_ptr<Impl> impl_;
     public:
     Simple();
     ~Simple();
     int get() const;
};

但这在您使用名称空间的现实世界中似乎失败了。 当存在命名空间时,前向声明必须移到 class 声明之前。谁能解释一下为什么?

// Simple.h but now with name spaces
namespace weired::example {
    struct Impl; // OK! forwad declaration must go here
    class Simple {
         //struct Impl; // ERROR! incomplete type in ctor definition
         std::unique_ptr<Impl> impl_;
         public:
         Simple();
         ~Simple();
         int get() const;
    };
}

我已经用 gcc9clang8 测试过 -std=c++11c++2a。 为了完整起见,这里有 Simple.cpp 和 main.cpp 文件,因此您可以自己 运行 这个例子:

// Simple.cpp
#include "Simple.h"
namespace weired::example {

    struct Impl {int a,b;};

    Simple::Simple() : impl_(new Impl{3,4}) {}       

    Simple::~Simple() = default;

    int Simple::get() const
    {
        return impl_->a;
    }
}

// main.cpp
#include "Simple.h"
#include <iostream>

int main () {       
        auto nonsense = weired::example::Simple{};
        std::cout << nonsense.get() << '\n';
}   

您可以在 class 内部和 class 外部转发声明 Impl,无论 Simple 是在命名空间中定义还是全局定义。

Impl 的实现在两种情况下几乎相同。

没有namespace

选项 1(Impl 是同行 class)

.h 文件

struct Impl;
class Simple { ... };

.cpp 文件

// Define Impl
struct Impl { ... };

// Define Simple

选项 2(Impl 是嵌套 class)

.h 文件

class Simple
{
   struct Impl;
   ...
};

.cpp 文件

// Define Impl
struct Simple::Impl { ... };  // Need the Simple:: scope here.

// Define Simple

namespace

选项 1(Impl 是同行 class)

.h 文件

namespace weired::example {

   struct Impl;
   class Simple { ... };
}

.cpp 文件

namespace weired::example {

   // Define Impl
   struct Impl { ... };

   // Define Simple
}

选项 2(Impl 是嵌套 class)

.h 文件

namespace weired::example {

   class Simple
   {
      struct Impl;
      ...
   };
}

.cpp 文件

namespace weired::example {

   // Define Impl
   struct Simple::Impl { ... };  // Need the Simple:: scope here.

   // Define Simple
}