How can I reuse the same impl destructor in a class derived from a PIMPL base class?
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
add a comment |
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
The problem is the conversion ofnullptr
into astd::unique_ptr<Base::Impl>
happening outside of Base.cpp whereImpl
is fully defined. This leads to the reason why I'm not trying to answer this: I can't provide a good solution. If onlyBase
knows how to make aImpl
, how willDerived
ever get anImpl
to supply toBase
? 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, theDerived
class in my use case is a mock, so I have no need to construct anImpl
.
– srujzs
Nov 19 '18 at 23:57
Advice would be don't then. Add another constructor toBase
.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
add a comment |
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
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
c++ inheritance unique-ptr pimpl-idiom
edited Nov 19 '18 at 23:39
srujzs
asked Nov 19 '18 at 23:07
srujzssrujzs
6417
6417
The problem is the conversion ofnullptr
into astd::unique_ptr<Base::Impl>
happening outside of Base.cpp whereImpl
is fully defined. This leads to the reason why I'm not trying to answer this: I can't provide a good solution. If onlyBase
knows how to make aImpl
, how willDerived
ever get anImpl
to supply toBase
? 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, theDerived
class in my use case is a mock, so I have no need to construct anImpl
.
– srujzs
Nov 19 '18 at 23:57
Advice would be don't then. Add another constructor toBase
.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
add a comment |
The problem is the conversion ofnullptr
into astd::unique_ptr<Base::Impl>
happening outside of Base.cpp whereImpl
is fully defined. This leads to the reason why I'm not trying to answer this: I can't provide a good solution. If onlyBase
knows how to make aImpl
, how willDerived
ever get anImpl
to supply toBase
? 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, theDerived
class in my use case is a mock, so I have no need to construct anImpl
.
– srujzs
Nov 19 '18 at 23:57
Advice would be don't then. Add another constructor toBase
.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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
The problem is the conversion of
nullptr
into astd::unique_ptr<Base::Impl>
happening outside of Base.cpp whereImpl
is fully defined. This leads to the reason why I'm not trying to answer this: I can't provide a good solution. If onlyBase
knows how to make aImpl
, how willDerived
ever get anImpl
to supply toBase
? 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 anImpl
.– 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