2

Most Pimpl examples look as follows:

UPDATE: both cases fail, i.e. with and without namespaces. See answer from R Sahu at https://stackoverflow.com/a/57103016/2712726 . class Impl must be qualified with class name Simpl

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

But this seems to fail in the real world where you would use namespaces. When namespaces are present, then the forward declaration has to be moved before the class declaration. Can anyone explain why?

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

I have tested this with gcc9 and clang8 with -std=c++11 up to c++2a. Just for completeness, here are the Simple.cpp and main.cpp files so you can run the example yourself:

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

and

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

int main () {       
        auto nonsense = weired::example::Simple{};
        std::cout << nonsense.get() << '\n';
}   
Patrick Fromberg
  • 1,313
  • 11
  • 37
  • 1
    The inner class approach will still work inside a namespace; You may just be defining the inner class in the .cpp file incorrectly. What are the actual errors that you are getting? – Romen Jul 18 '19 at 21:33
  • 1
    `struct Simple::Impl {int a,b;};` in the Simple.cpp instead of `struct Impl {int a,b;};` ought to do the job. – user4581301 Jul 18 '19 at 21:35
  • Mind you, I can't do the nested namespace . That a C++20 thing? – user4581301 Jul 18 '19 at 21:36
  • 1
    @user4581301 C++17 thing. – Fureeish Jul 18 '19 at 21:47
  • @Romen, the code is complete, you can run it on your computer. The error message is `invalid use of incomplete type` where the smart pointer is instantiated in the constructor of the Simple class. – Patrick Fromberg Jul 18 '19 at 21:47
  • @user4581301, no I tested all standards from 11 to 20. – Patrick Fromberg Jul 18 '19 at 21:48
  • @user4581301, you where right with your comment regarding `struct Simple::Impl` that did not work at first when I tested. The reason is that I also had a struct/class missmatch between forward delcaration and definition. That usually does not matter. – Patrick Fromberg Jul 18 '19 at 21:53
  • 1
    @Fureeish Huh. Looks like at some point I set the default standard in my IDE's new project template to 11 instead of 17. Thanks! Fixed. – user4581301 Jul 18 '19 at 22:07
  • @PatrickFromberg glad you're on track. Also glad this question showed me that my IDE was misconfigured. That'll save me some time later, probably when it's important. – user4581301 Jul 18 '19 at 22:10

1 Answers1

2

You may forward declare Impl inside the class and outside the class, regardless of whether Simple is defined in a namespace or globally.

The implementation of Impl is almost identical in both cases.

Without namespace

Option 1 (Impl is a peer class)

The .h file

struct Impl;
class Simple { ... };

The .cpp file

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

// Define Simple

Option 2 (Impl is a nested class)

The .h file

class Simple
{
   struct Impl;
   ...
};

The .cpp file

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

// Define Simple

With namespace

Option 1 (Impl is a peer class)

The .h file

namespace weired::example {

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

The .cpp file

namespace weired::example {

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

   // Define Simple
}

Option 2 (Impl is a nested class)

The .h file

namespace weired::example {

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

The .cpp file

namespace weired::example {

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

   // Define Simple
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Ah yes, the idea is to forward declare in the class privately and then you do not need to protect the Impl class in a `details` namespace to make it invisible to clients. But in that case, why can I omit the Simple qualifier in `struct Simple::Impl{...}` if my Simple class is not in a name space? Compiler error in all compilers? – Patrick Fromberg Jul 18 '19 at 22:03
  • You are correct, namespace case and non namespace case are identical. I cannot reproduce my successful compilation without name space anymore. Must have been a typo somewhere. Sorry about that. – Patrick Fromberg Jul 18 '19 at 22:29