unique_ptr со стертым типом деструктора не совсем работает (работает с предупреждениями)

Есть хорошая маленькая техника Вот разрешить использование 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 как указано выше и компилировать.

Вопрос в том, что происходит? Может ли быть какая-либо альтернативная реализация, чтобы обойти это предупреждение?

4

Решение

Ваш вопрос верен, но код, который вы пытаетесь использовать, немного запутан, поэтому позвольте мне подойти к поиску желаемого решения.

  • То, что вы здесь делаете, не является стиранием типа (и Анджей тоже ошибается) — вы просто фиксируете удаление в значении функции времени выполнения, без какой-либо выгоды, кстати. Стирание типа OTH — это когда другие части кода теряют информацию о начальном типе.
  • Я попробовал ваш код на VS 2015, который также вызывает деструктор ~Foo()Однако это только большая удача, а это значит, что компилятор делает какие-то модные вещи. Если вы не используете std :: function, а пишете свой собственный пользовательский удалитель, то деструктор не вызывается.

Решение 1 — тип стирания

Если вы действительно хотите стереть тип, вы должны написать / использовать 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,

Решение 2 — неполные типы, действительно

На самом деле вы действительно хотите иметь дело с неполными типами. Проблема, с которой вы столкнулись при использовании программы удаления 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
}
2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]