Есть хорошая маленькая техника Вот разрешить использование std::unique_ptr
с неполными типами.
Вот соответствующий код:
// File: erasedptr.h
#include <memory>
#include <functional>
// type erased deletor (an implementation type using "veneer")
template <typename T>
struct ErasedDeleter : std::function<void(T*)>
{
ErasedDeleter()
: std::function<void(T*)>( [](T * p) {delete p;} )
{}
};
// A unique_ptr typedef
template <typename T>
using ErasedPtr = std::unique_ptr<T, ErasedDeleter<T>>;// Declare stuff with an incomplete type
struct Foo;
ErasedPtr<Foo> makeFoo();// File: main.cpp (Foo's definition is not available in this translation unit)
#include "erasedptr.h"int main() {
ErasedPtr<Foo> f; // [R1]
f = makeFoo();
// ~Foo() gets called fine
}
// File: foo.cpp
#include <iostream>
#include "erasedptr.h"struct Foo {
~Foo() { std::cout << "~Foo()\n" ; }
};
ErasedPtr<Foo> makeFoo() { return ErasedPtr<Foo>(new Foo); }
Это работает на всех компиляторах, которые я пробовал: gcc 4.9, clang 3.5 и msvc VS13 и VS15. Но все они генерируют следующее предупреждение:
deletion of pointer to incomplete type 'Foo'; no destructor called
Если [R1] выше заменено на ErasedPtr<Foo> f( makeFoo() );
предупреждение не проявляется.
В конце концов, деструктор делает позвонить, и, кажется, нет реальной проблемы. Предупреждение является проблематичным, поскольку его нельзя игнорировать в критических по качеству средах, и этот очень полезный шаблон в противном случае недоступен.
Чтобы воспроизвести, создайте 3 файла erasedptr.hpp
, main.cpp
, foo.cpp
как указано выше и компилировать.
Вопрос в том, что происходит? Может ли быть какая-либо альтернативная реализация, чтобы обойти это предупреждение?
Ваш вопрос верен, но код, который вы пытаетесь использовать, немного запутан, поэтому позвольте мне подойти к поиску желаемого решения.
~Foo()
Однако это только большая удача, а это значит, что компилятор делает какие-то модные вещи. Если вы не используете std :: function, а пишете свой собственный пользовательский удалитель, то деструктор не вызывается.Если вы действительно хотите стереть тип, вы должны написать / использовать ErasedPtr следующим образом:
erasedptr.h
// file erasedptr.h
#include <memory>
#include <functional>
// make type erased deleter
template <typename T>
std::function<void(void*)> makeErasedDeleter()
{
return {
[](void* p) {
delete static_cast<T*>(p);
}
};
};
// A unique_ptr typedef
template <typename T>
using ErasedPtr = std::unique_ptr<T, std::function<void(void*)>>;
foo.cpp
// file foo.cpp
#include <iostream>
#include "erasedptr.h."
struct Foo {
~Foo() { std::cout << "~Foo()\n" ; }
};
// capture creation and deletion of Foo in this translation unit
ErasedPtr<Foo> makeFoo() {
return { new Foo, makeErasedDeleter<Foo>() };
}
main.cpp
// file main.cpp (Foo's definition is not available in this translation unit)
#include "erasedptr.h"
// fwd decl Foo
struct Foo;
ErasedPtr<Foo> makeFoo();
int main() {
ErasedPtr<Foo> f; // [R1]
f = makeFoo();
// ~Foo() gets called fine
}
Только так foo.cpp должен знать о фактическом типе, и он фиксирует удаление в std::function
,
На самом деле вы действительно хотите иметь дело с неполными типами. Проблема, с которой вы столкнулись при использовании программы удаления STL по умолчанию std::default_delete
является то, что он утверждает во время компиляции, безопасно ли удаление — и это чертовски правильно!
Чтобы заставить его работать правильно, нужно сказать компилятору / компоновщику, что вы действительно заботились о правильной реализации удаления, используя явную реализацию шаблона. Таким образом, вам не нужен специальный псевдоним typedef / template для вашего уникального указателя:
foo.cpp
// file foo.cpp
#include "foo_fwddecl.h"
// capture creation of Foo in this translation unit
std::unique_ptr<Foo> makeFoo() {
return std::make_unique<Foo>();
}
// explicitly instantiate deletion of Foo in this translation unit
template void std::default_delete<Foo>::operator()(Foo*) const noexcept;
template void std::default_delete<const Foo>::operator()(const Foo*) const noexcept;
// note: possibly instantiate for volatile/const volatile modifiers
foo_fwddecl.h
#include <memory>
struct Foo;
std::unique_ptr<Foo> makeFoo();
extern template void std::default_delete<Foo>::operator()(Foo*) const noexcept;
extern template void std::default_delete<const Foo>::operator()(const Foo*) const noexcept;
// note: possibly instantiate for volatile/const volatile modifiers
main.cpp
// file main.cpp (Foo's definition is not available in this translation unit)
#include "foo_fwddecl.h"
int main() {
std::unique_ptr<Foo> f; // [R1]
f = makeFoo();
// ~Foo() gets called fine
}
Других решений пока нет …