How can I reuse the same impl destructor in a class derived from a PIMPL base class?












1















I'm trying to create a class that inherits from a base class that uses the PIMPL idiom.



I have a base class base.h:



#include <memory>

class Base {
public:
class Impl;
Base(std::unique_ptr<Base::Impl> impl);
virtual ~Base();

private:
std::unique_ptr<Base::Impl> impl_;
};


which is implemented in base.cpp:



#include "base.h"

class Base::Impl {
public:
Impl() {}
~Impl() {}
};

Base::Base(std::unique_ptr<Base::Impl> impl) : impl_(std::move(impl)) {}
Base::~Base() {}


which is then derived in derived.cpp:



#include "base.h"

class Derived : public Base {
public:
Derived() : Base(nullptr) {}
~Derived() override {}
};


When I compile these three files, I get the following error:



In file included from /usr/include/c++/7/memory:80:0,
from base.h:1,
from derived.cpp:1:
/usr/include/c++/7/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Base::Impl]’:
/usr/include/c++/7/bits/unique_ptr.h:268:17: required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Base::Impl; _Dp = std::default_delete<Base::Impl>]’
derived.cpp:5:27: required from here
/usr/include/c++/7/bits/unique_ptr.h:76:22: error: invalid application of ‘sizeof’ to incomplete type ‘Base::Impl’
static_assert(sizeof(_Tp)>0,


which kind of makes sense, since there is no ~Impl() for the derived class to use. After I add the following snippet to derived.cpp before the Derived class definition:



class Base::Impl {
public:
Impl() {}
~Impl() {}
};


it compiles fine. Is there any way for derived.cpp to use the Impl destructor from base.cpp without adding it to some other shared file that both base.cpp and derived.cpp use? I've also noticed that this issue only arises because of the constructor taking in a unique_ptr to Base::Impl. If I replace that constructor with an empty constructor that just assigns a new Impl to Base::impl_, I don't have to include the destructor like I had to above. What's different in that situation?










share|improve this question

























  • The problem is the conversion of nullptr into a std::unique_ptr<Base::Impl> happening outside of Base.cpp where Impl is fully defined. This leads to the reason why I'm not trying to answer this: I can't provide a good solution. If only Base knows how to make a Impl, how will Derived ever get an Impl to supply to Base? This constructor seems minimally useful. Or maybe I do have a solution.... Lemme think.

    – user4581301
    Nov 19 '18 at 23:51











  • It does seem a bit strange, but for context, the Derived class in my use case is a mock, so I have no need to construct an Impl.

    – srujzs
    Nov 19 '18 at 23:57











  • Advice would be don't then. Add another constructor to Base. Base::Base() : impl_(nullptr) {} That said, I hate hate hate modifying a class to support a test. It means you are not testing the proper use-cases of the system under test and the test results may be questionable.

    – user4581301
    Nov 20 '18 at 0:08











  • Thinking didn't coalesces into a decent solution, by the way.

    – user4581301
    Nov 20 '18 at 0:09
















1















I'm trying to create a class that inherits from a base class that uses the PIMPL idiom.



I have a base class base.h:



#include <memory>

class Base {
public:
class Impl;
Base(std::unique_ptr<Base::Impl> impl);
virtual ~Base();

private:
std::unique_ptr<Base::Impl> impl_;
};


which is implemented in base.cpp:



#include "base.h"

class Base::Impl {
public:
Impl() {}
~Impl() {}
};

Base::Base(std::unique_ptr<Base::Impl> impl) : impl_(std::move(impl)) {}
Base::~Base() {}


which is then derived in derived.cpp:



#include "base.h"

class Derived : public Base {
public:
Derived() : Base(nullptr) {}
~Derived() override {}
};


When I compile these three files, I get the following error:



In file included from /usr/include/c++/7/memory:80:0,
from base.h:1,
from derived.cpp:1:
/usr/include/c++/7/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Base::Impl]’:
/usr/include/c++/7/bits/unique_ptr.h:268:17: required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Base::Impl; _Dp = std::default_delete<Base::Impl>]’
derived.cpp:5:27: required from here
/usr/include/c++/7/bits/unique_ptr.h:76:22: error: invalid application of ‘sizeof’ to incomplete type ‘Base::Impl’
static_assert(sizeof(_Tp)>0,


which kind of makes sense, since there is no ~Impl() for the derived class to use. After I add the following snippet to derived.cpp before the Derived class definition:



class Base::Impl {
public:
Impl() {}
~Impl() {}
};


it compiles fine. Is there any way for derived.cpp to use the Impl destructor from base.cpp without adding it to some other shared file that both base.cpp and derived.cpp use? I've also noticed that this issue only arises because of the constructor taking in a unique_ptr to Base::Impl. If I replace that constructor with an empty constructor that just assigns a new Impl to Base::impl_, I don't have to include the destructor like I had to above. What's different in that situation?










share|improve this question

























  • The problem is the conversion of nullptr into a std::unique_ptr<Base::Impl> happening outside of Base.cpp where Impl is fully defined. This leads to the reason why I'm not trying to answer this: I can't provide a good solution. If only Base knows how to make a Impl, how will Derived ever get an Impl to supply to Base? This constructor seems minimally useful. Or maybe I do have a solution.... Lemme think.

    – user4581301
    Nov 19 '18 at 23:51











  • It does seem a bit strange, but for context, the Derived class in my use case is a mock, so I have no need to construct an Impl.

    – srujzs
    Nov 19 '18 at 23:57











  • Advice would be don't then. Add another constructor to Base. Base::Base() : impl_(nullptr) {} That said, I hate hate hate modifying a class to support a test. It means you are not testing the proper use-cases of the system under test and the test results may be questionable.

    – user4581301
    Nov 20 '18 at 0:08











  • Thinking didn't coalesces into a decent solution, by the way.

    – user4581301
    Nov 20 '18 at 0:09














1












1








1


1






I'm trying to create a class that inherits from a base class that uses the PIMPL idiom.



I have a base class base.h:



#include <memory>

class Base {
public:
class Impl;
Base(std::unique_ptr<Base::Impl> impl);
virtual ~Base();

private:
std::unique_ptr<Base::Impl> impl_;
};


which is implemented in base.cpp:



#include "base.h"

class Base::Impl {
public:
Impl() {}
~Impl() {}
};

Base::Base(std::unique_ptr<Base::Impl> impl) : impl_(std::move(impl)) {}
Base::~Base() {}


which is then derived in derived.cpp:



#include "base.h"

class Derived : public Base {
public:
Derived() : Base(nullptr) {}
~Derived() override {}
};


When I compile these three files, I get the following error:



In file included from /usr/include/c++/7/memory:80:0,
from base.h:1,
from derived.cpp:1:
/usr/include/c++/7/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Base::Impl]’:
/usr/include/c++/7/bits/unique_ptr.h:268:17: required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Base::Impl; _Dp = std::default_delete<Base::Impl>]’
derived.cpp:5:27: required from here
/usr/include/c++/7/bits/unique_ptr.h:76:22: error: invalid application of ‘sizeof’ to incomplete type ‘Base::Impl’
static_assert(sizeof(_Tp)>0,


which kind of makes sense, since there is no ~Impl() for the derived class to use. After I add the following snippet to derived.cpp before the Derived class definition:



class Base::Impl {
public:
Impl() {}
~Impl() {}
};


it compiles fine. Is there any way for derived.cpp to use the Impl destructor from base.cpp without adding it to some other shared file that both base.cpp and derived.cpp use? I've also noticed that this issue only arises because of the constructor taking in a unique_ptr to Base::Impl. If I replace that constructor with an empty constructor that just assigns a new Impl to Base::impl_, I don't have to include the destructor like I had to above. What's different in that situation?










share|improve this question
















I'm trying to create a class that inherits from a base class that uses the PIMPL idiom.



I have a base class base.h:



#include <memory>

class Base {
public:
class Impl;
Base(std::unique_ptr<Base::Impl> impl);
virtual ~Base();

private:
std::unique_ptr<Base::Impl> impl_;
};


which is implemented in base.cpp:



#include "base.h"

class Base::Impl {
public:
Impl() {}
~Impl() {}
};

Base::Base(std::unique_ptr<Base::Impl> impl) : impl_(std::move(impl)) {}
Base::~Base() {}


which is then derived in derived.cpp:



#include "base.h"

class Derived : public Base {
public:
Derived() : Base(nullptr) {}
~Derived() override {}
};


When I compile these three files, I get the following error:



In file included from /usr/include/c++/7/memory:80:0,
from base.h:1,
from derived.cpp:1:
/usr/include/c++/7/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Base::Impl]’:
/usr/include/c++/7/bits/unique_ptr.h:268:17: required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Base::Impl; _Dp = std::default_delete<Base::Impl>]’
derived.cpp:5:27: required from here
/usr/include/c++/7/bits/unique_ptr.h:76:22: error: invalid application of ‘sizeof’ to incomplete type ‘Base::Impl’
static_assert(sizeof(_Tp)>0,


which kind of makes sense, since there is no ~Impl() for the derived class to use. After I add the following snippet to derived.cpp before the Derived class definition:



class Base::Impl {
public:
Impl() {}
~Impl() {}
};


it compiles fine. Is there any way for derived.cpp to use the Impl destructor from base.cpp without adding it to some other shared file that both base.cpp and derived.cpp use? I've also noticed that this issue only arises because of the constructor taking in a unique_ptr to Base::Impl. If I replace that constructor with an empty constructor that just assigns a new Impl to Base::impl_, I don't have to include the destructor like I had to above. What's different in that situation?







c++ inheritance unique-ptr pimpl-idiom






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 19 '18 at 23:39







srujzs

















asked Nov 19 '18 at 23:07









srujzssrujzs

6417




6417













  • The problem is the conversion of nullptr into a std::unique_ptr<Base::Impl> happening outside of Base.cpp where Impl is fully defined. This leads to the reason why I'm not trying to answer this: I can't provide a good solution. If only Base knows how to make a Impl, how will Derived ever get an Impl to supply to Base? This constructor seems minimally useful. Or maybe I do have a solution.... Lemme think.

    – user4581301
    Nov 19 '18 at 23:51











  • It does seem a bit strange, but for context, the Derived class in my use case is a mock, so I have no need to construct an Impl.

    – srujzs
    Nov 19 '18 at 23:57











  • Advice would be don't then. Add another constructor to Base. Base::Base() : impl_(nullptr) {} That said, I hate hate hate modifying a class to support a test. It means you are not testing the proper use-cases of the system under test and the test results may be questionable.

    – user4581301
    Nov 20 '18 at 0:08











  • Thinking didn't coalesces into a decent solution, by the way.

    – user4581301
    Nov 20 '18 at 0:09



















  • The problem is the conversion of nullptr into a std::unique_ptr<Base::Impl> happening outside of Base.cpp where Impl is fully defined. This leads to the reason why I'm not trying to answer this: I can't provide a good solution. If only Base knows how to make a Impl, how will Derived ever get an Impl to supply to Base? This constructor seems minimally useful. Or maybe I do have a solution.... Lemme think.

    – user4581301
    Nov 19 '18 at 23:51











  • It does seem a bit strange, but for context, the Derived class in my use case is a mock, so I have no need to construct an Impl.

    – srujzs
    Nov 19 '18 at 23:57











  • Advice would be don't then. Add another constructor to Base. Base::Base() : impl_(nullptr) {} That said, I hate hate hate modifying a class to support a test. It means you are not testing the proper use-cases of the system under test and the test results may be questionable.

    – user4581301
    Nov 20 '18 at 0:08











  • Thinking didn't coalesces into a decent solution, by the way.

    – user4581301
    Nov 20 '18 at 0:09

















The problem is the conversion of nullptr into a std::unique_ptr<Base::Impl> happening outside of Base.cpp where Impl is fully defined. This leads to the reason why I'm not trying to answer this: I can't provide a good solution. If only Base knows how to make a Impl, how will Derived ever get an Impl to supply to Base? This constructor seems minimally useful. Or maybe I do have a solution.... Lemme think.

– user4581301
Nov 19 '18 at 23:51





The problem is the conversion of nullptr into a std::unique_ptr<Base::Impl> happening outside of Base.cpp where Impl is fully defined. This leads to the reason why I'm not trying to answer this: I can't provide a good solution. If only Base knows how to make a Impl, how will Derived ever get an Impl to supply to Base? This constructor seems minimally useful. Or maybe I do have a solution.... Lemme think.

– user4581301
Nov 19 '18 at 23:51













It does seem a bit strange, but for context, the Derived class in my use case is a mock, so I have no need to construct an Impl.

– srujzs
Nov 19 '18 at 23:57





It does seem a bit strange, but for context, the Derived class in my use case is a mock, so I have no need to construct an Impl.

– srujzs
Nov 19 '18 at 23:57













Advice would be don't then. Add another constructor to Base. Base::Base() : impl_(nullptr) {} That said, I hate hate hate modifying a class to support a test. It means you are not testing the proper use-cases of the system under test and the test results may be questionable.

– user4581301
Nov 20 '18 at 0:08





Advice would be don't then. Add another constructor to Base. Base::Base() : impl_(nullptr) {} That said, I hate hate hate modifying a class to support a test. It means you are not testing the proper use-cases of the system under test and the test results may be questionable.

– user4581301
Nov 20 '18 at 0:08













Thinking didn't coalesces into a decent solution, by the way.

– user4581301
Nov 20 '18 at 0:09





Thinking didn't coalesces into a decent solution, by the way.

– user4581301
Nov 20 '18 at 0:09












0






active

oldest

votes











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53383923%2fhow-can-i-reuse-the-same-impl-destructor-in-a-class-derived-from-a-pimpl-base-cl%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























0






active

oldest

votes








0






active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53383923%2fhow-can-i-reuse-the-same-impl-destructor-in-a-class-derived-from-a-pimpl-base-cl%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Guess what letter conforming each word

Port of Spain

Run scheduled task as local user group (not BUILTIN)