불완전한 유형의 std :: unique_ptr은 컴파일되지 않습니다
나는 pimpl-idiom을 std::unique_ptr
다음 과 함께 사용하고 있습니다 :
class window {
window(const rectangle& rect);
private:
class window_impl; // defined elsewhere
std::unique_ptr<window_impl> impl_; // won't compile
};
그러나 304 줄에서 불완전한 유형의 사용과 관련하여 컴파일 오류가 발생합니다 <memory>
.
sizeof
불완전한 유형 'uixx::window::window_impl
' 에 ' '을 (를) 잘못 적용했습니다.
내가 아는 std::unique_ptr
한 불완전한 유형으로 사용할 수 있어야합니다. 이것은 libc ++의 버그입니까? 아니면 여기서 잘못하고 있습니까?
다음은 std::unique_ptr
불완전한 유형의 예입니다 . 문제는 파괴에 있습니다.
와 함께 pimpl을 사용 unique_ptr
하는 경우 소멸자를 선언해야합니다.
class foo
{
class impl;
std::unique_ptr<impl> impl_;
public:
foo(); // You may need a def. constructor to be defined elsewhere
~foo(); // Implement (with {}, or with = default;) where impl is complete
};
그렇지 않으면 컴파일러가 기본 컴파일러를 생성하므로 이에 대한 완전한 선언이 foo::impl
필요합니다.
템플릿 생성자가있는 경우 impl_
멤버를 생성하지 않아도 문제가 발생합니다 .
template <typename T>
foo::foo(T bar)
{
// Here the compiler needs to know how to
// destroy impl_ in case an exception is
// thrown !
}
네임 스페이스 범위에서 다음을 사용 unique_ptr
하면 작동하지 않습니다.
class impl;
std::unique_ptr<impl> impl_;
컴파일러는이 정적 지속 기간 객체를 파괴하는 방법을 여기에서 알아야하기 때문에. 해결 방법은 다음과 같습니다.
class impl;
struct ptr_impl : std::unique_ptr<impl>
{
~ptr_impl(); // Implement (empty body) elsewhere
} impl_;
으로 알렉산더 C.가 언급 한 문제가 내려 오는 window
유형이 곳의 소멸자가 암시 장소에서 정의되는 window_impl
불완전 아직도있다. 그의 솔루션 외에도 내가 사용한 또 다른 해결 방법은 헤더에 Deleter functor를 선언하는 것입니다.
// Foo.h
class FooImpl;
struct FooImplDeleter
{
void operator()(FooImpl *p);
}
class Foo
{
...
private:
std::unique_ptr<FooImpl, FooImplDeleter> impl_;
};
// Foo.cpp
...
void FooImplDeleter::operator()(FooImpl *p)
{
delete p;
}
맞춤 삭제 도구 사용
The problem is that unique_ptr<T>
must call the destructor T::~T()
in its own destructor, its move assignment operator, and unique_ptr::reset()
member function (only). However, these must be called (implicitly or explicitly) in several PIMPL situations (already in the outer class's destructor and move assignment operator).
As already pointed out in another answer, one way to avoid that is to move all operations that require unique_ptr::~unique_ptr()
, unique_ptr::operator=(unique_ptr&&)
, and unique_ptr::reset()
into the source file where the pimpl helper class is actually defined.
However, this is rather inconvenient and defies the very point of the pimpl idoim to some degree. A much cleaner solution that avoids all that is to use a custom deleter and only move its definition into the source file where the pimple helper class lives. Here is a simple example:
// file.h
class foo
{
struct pimpl;
struct pimpl_deleter { void operator()(pimpl*) const; };
std::unique_ptr<pimpl,pimpl_deleter> m_pimpl;
public:
foo(some data);
foo(foo&&) = default; // no need to define this in file.cc
foo&operator=(foo&&) = default; // no need to define this in file.cc
//foo::~foo() auto-generated: no need to define this in file.cc
};
// file.cc
struct foo::pimpl
{
// lots of complicated code
};
void foo::pimpl_deleter::operator()(foo::pimpl*ptr) const { delete ptr; }
Instead of a separate deleter class, you can also use a free function or static
member of foo
in conjunction with a lambda:
class foo {
struct pimpl;
static void delete_pimpl(pimpl*);
std::unique_ptr<pimpl,[](pimpl*ptr){delete_pimpl(ptr);}> m_pimpl;
};
Probably you have some function bodies within .h file within class that uses incomplete type.
Make sure that within your .h for class window you have only function declaration. All function bodies for window must be in .cpp file. And for window_impl as well...
Btw, you have to explicitly add destructor declaration for windows class in your .h file.
But you CANNOT put empty dtor body in you header file:
class window {
virtual ~window() {};
}
Must be just a declaration:
class window {
virtual ~window();
}
To add to the other's replies about the custom deleter, in our internal "utilities library" I added a helper header to implement this common pattern (std::unique_ptr
of an incomplete type, known only to some of the TU to e.g. avoid long compile times or to provide just an opaque handle to clients).
It provides the common scaffolding for this pattern: a custom deleter class that invokes an externally-defined deleter function, a type alias for a unique_ptr
with this deleter class, and a macro to declare the deleter function in a TU that has a complete definition of the type. I think that this has some general usefulness, so here it is:
#ifndef CZU_UNIQUE_OPAQUE_HPP
#define CZU_UNIQUE_OPAQUE_HPP
#include <memory>
/**
Helper to define a `std::unique_ptr` that works just with a forward
declaration
The "regular" `std::unique_ptr<T>` requires the full definition of `T` to be
available, as it has to emit calls to `delete` in every TU that may use it.
A workaround to this problem is to have a `std::unique_ptr` with a custom
deleter, which is defined in a TU that knows the full definition of `T`.
This header standardizes and generalizes this trick. The usage is quite
simple:
- everywhere you would have used `std::unique_ptr<T>`, use
`czu::unique_opaque<T>`; it will work just fine with `T` being a forward
declaration;
- in a TU that knows the full definition of `T`, at top level invoke the
macro `CZU_DEFINE_OPAQUE_DELETER`; it will define the custom deleter used
by `czu::unique_opaque<T>`
*/
namespace czu {
template<typename T>
struct opaque_deleter {
void operator()(T *it) {
void opaque_deleter_hook(T *);
opaque_deleter_hook(it);
}
};
template<typename T>
using unique_opaque = std::unique_ptr<T, opaque_deleter<T>>;
}
/// Call at top level in a C++ file to enable type %T to be used in an %unique_opaque<T>
#define CZU_DEFINE_OPAQUE_DELETER(T) namespace czu { void opaque_deleter_hook(T *it) { delete it; } }
#endif
May be not a best solution, but sometimes you may use shared_ptr instead. If course it's a bit an overkill, but... as for unique_ptr, I'll perhaps wait 10 years more until C++ standard makers will decide to use lambda as a deleter.
Another side. Per your code it may happen, that on destruction stage window_impl will be incomplete. This could be a reason of undefined behaviour. See this: Why, really, deleting an incomplete type is undefined behaviour?
So, if possible I would define a very base object to all your objects, with virtual destructor. And you're almost good. You just should keep in mind that system will call virtual destructor for your pointer, so you should define it for every ancestor. You should also define base class in inheritance section as a virtual (see this for details).
참고URL : https://stackoverflow.com/questions/9954518/stdunique-ptr-with-an-incomplete-type-wont-compile
'Programing' 카테고리의 다른 글
기본 동작을 중단하지 않고 파이썬에서 __getattr__을 어떻게 재정의합니까? (0) | 2020.05.22 |
---|---|
SQL Server에 이미지를 저장 하시겠습니까? (0) | 2020.05.22 |
Android Studio 2.0에서 즉시 실행 (끄는 방법) (0) | 2020.05.22 |
장고-쿼리 결과 제한 (0) | 2020.05.22 |
Injectable 클래스가 인스턴스화 될 때 ngOnInit가 호출되지 않음 (0) | 2020.05.22 |