Я использую идиому PIMPL и, в частности, использую шаблон, предоставленный эта почта. Учитывая набор классов ниже и компиляцию с VS2015 Update 3, я получаю ошибки компиляции:
Ошибка C2027 использование неопределенного типа ‘C :: C_impl’ (компиляция исходного файла src \ A.cpp)
Ошибка C2338 не может удалить неполный тип (компиляция исходного файла src \ A.cpp)
Предупреждение C4150 удаление указателя на неполный тип ‘C :: C_impl’; не вызывается деструктор (компиляция исходного файла src \ A.cpp)
Я могу решить это, раскомментировав C::~C()
что заставляет меня верить, что что-то мешает ~C()
быть автоматически сгенерированным, но я не понимаю, что. В соответствии с эта ссылка, деструктор для типа T неявно определяется как удаленный, если выполняется любое из следующих условий:
- T имеет нестатический элемент данных, который не может быть уничтожен (удален или недоступен деструктор)
- T имеет прямой или виртуальный базовый класс, который не может быть уничтожен (имеет удаленные или недоступные деструкторы)
- T является объединением и имеет вариантный член с нетривиальным деструктором.
- Неявно объявленный деструктор является виртуальным (поскольку базовый класс имеет виртуальный деструктор), и поиск функции освобождения (оператор delete () приводит к вызову неоднозначной, удаленной или недоступной функции).
Пункты № 2, 3 и 4, очевидно, не относятся к C
и я не верю, что № 1 применяется, потому что pimpl<>
(C
единственный член) явно определяет деструктор.
Может кто-нибудь объяснить, пожалуйста, что происходит?
хиджры
#pragma once
#include <Pimpl.h>
class A
{
private:
struct A_impl;
pimpl<A_impl> m_pimpl;
};
B.h
#pragma once
#include "C.h"
class B
{
private:
C m_C;
};
C.h
#pragma once
#include <Pimpl.h>
class C
{
public:
// Needed for the PIMPL pattern
//~C();
private:
struct C_impl;
pimpl<C_impl> m_pimpl;
};
a.cpp
#include <memory>
#include "A.h"#include "B.h"#include <PimplImpl.h>
struct A::A_impl
{
std::unique_ptr<B> m_pB;
};
// Ensure all the code for the template is compiled
template class pimpl<A::A_impl>;
C.cpp
#include <C.h>
#include <PimplImpl.h>
struct C::C_impl { };
// Needed for the PIMPL pattern
//C::~C() = default;
// Ensure all the code for the template is compiled
template class pimpl<C::C_impl>;
Для полноты реализации PIMPL из поста, упомянутого выше:
pimpl.h
#pragma once
#include <memory>
template<typename T>
class pimpl
{
private:
std::unique_ptr<T> m;
public:
pimpl();
template<typename ...Args> pimpl(Args&& ...);
~pimpl();
T* operator->();
T& operator*();
};
PimplImpl.h
#pragma once
#include <utility>
template<typename T>
pimpl<T>::pimpl() : m{ new T{} } {}
template<typename T>
template<typename ...Args>
pimpl<T>::pimpl(Args&& ...args)
: m{ new T{ std::forward<Args>(args)... } }
{
}
template<typename T>
pimpl<T>::~pimpl() {}
template<typename T>
T* pimpl<T>::operator->() { return m.get(); }
template<typename T>
T& pimpl<T>::operator*() { return *m.get(); }
Несколько замечаний по поводу кода выше:
A
а также C
потребителям моей библиотеки и хранить B
внутренний.Вы должны определить деструктор C вручную после определения C :: C_impl, потому что по умолчанию компилятор пытается сгенерировать деструктор C в точке использования, но это может быть точка, где определение C :: C_impl не может быть найдено (например, это может быть B.cpp).
Я считаю, что проблема в том, что std::unique_ptr
требует знания функции уничтожения (т. е. деструктора) во время создания экземпляра.
С деструктором по умолчанию ~C()
компилятор генерирует его в точке использования, что означает, что он пытается удалить pimpl<C_impl>
объект с доступной ему информацией в A.cpp
, Конечно, так как C_impl
объявляется только в тот момент, компилятор не знает, как уничтожить C_impl
объект, и это ошибка, которую вы получаете.
раскомментировав ~C();
говорит компилятору не беспокоиться о том, как уничтожить C_impl
, это будет определено где-то еще. В вашем случае это определено в C.cpp
где определение C_impl
известен.
В ответ на ваши изменения, деструктор для pimpl<C_impl>
имеет std::unique_ptr<C_impl>
который имеет нестатический член данных с недоступным деструктором (C_impl
является неполным типом в момент использования в рамках A.cpp
).