Here is a simplified code: https://godbolt.org/z/EnE76xMrP
The pImpl will contain a mutex member which make the pImpl neither copyable nor movable. But the Foo class has unique_ptr of pImpl as member which make Foo movable but not copyable.
class Foo final
{
public:
Foo();
~Foo();
void DoSomething(const int thread_num);
private:
int data{};
struct Impl;
std::unique_ptr<Impl> m_impl;
};
struct Foo::Impl final
{
mutable std::mutex m_mutex;
};
Foo::Foo()
: m_impl(std::make_unique<Impl>())
{}
Foo::~Foo() {
std::cout << "Foo dtor \n";
}
For mapping keys to Foo values, I used emplace. But it looks even constructing a pair<int, Foo> is not feasible. Would you please clarify ? Because the compiler message is somewhat cumbersome:
no matching function for call to 'std::pair<int, Foo>::pair(int, Foo)'
I tried to use shared_ptr instead of unique_ptr, and it works, but my question is why?
this compiles if you modify the class definition to have an explicitly defined move constructor.
working with Pimpl requires all "exposed" constructors and destructors to be declared in the headers, and implemented in the cpp.
class Foo final
{
public:
Foo();
~Foo();
Foo(Foo&&) noexcept;
Foo& operator=(Foo&&) noexcept;
void DoSomething(const int thread_num);
private:
int data{};
struct Impl;
std::unique_ptr<Impl> m_impl;
};
// in the cpp
Foo::Foo(): m_impl(std::make_unique<Impl>()) {};
Foo::~Foo() = default;
Foo::Foo(Foo&&) noexcept = default;
Foo& Foo::operator=(Foo&&) noexcept = default;
you also need a move assignment operator Foo& operator=(Foo&&)
if you ever intend to move assign it.
also note that you should use std::pair<int, Foo>
not std::pair<int, Foo&&>
as this rvalue Reference would be dangling, hence the SIGSEGV
Edit: the reason you need to explicitly define the move constructor is because unique_ptr
move constructor will try to invoke Impl
destructor which is only defined inside the cpp file.