2016-07-21 3 views
1

Я использую идиому PIMPL, и, в частности, я использую шаблон, предоставленный от this post. Учитывая множество классов ниже и компиляцию с VS2015 Update 3, я получаю ошибку компиляции:Почему этот тип является неполным при использовании идиомы PIMPL?

Ошибки C2027 использование неопределенного типа «C :: C_impl» (компиляции исходного файл \ SRC a.cpp)

Ошибка C2338 не может удалить неполный типа (компиляции исходного файла Src \ a.cpp)

предупреждения C4150 удаление указателя на незавершенный тип «C :: C_impl»; нет деструктора называется (компиляции исходного файла ЦСИ \ a.cpp)

Я не могу решить эту проблему раскомментировав C::~C(), что приводит меня к мысли, что что-то мешает ~C() от автоматического порождена, но я чего не понимаю. Согласно this reference, деструктор для типа T неявно определяется как удаляется, если какой-либо из следующих условий:

  1. Т имеет не статический элемент данных, который не может быть разрушенное производственное (был удален или недоступен деструктор)
  2. T имеет прямой или виртуальный базовый класс, который не может быть разрушен (удалены или недоступны деструкторы)
  3. T является объединением и имеет вариантный элемент с нетривиальным деструктором.
  4. неявно объявленная деструктор виртуальная (так как базовый класс имеет виртуальный деструктор) и поиск для функции Deallocation приводит к вызову неоднозначной, удалять или недоступную функцию.
(оператор удаления()

Предметы # 2, 3 и 4, очевидно, не применяются к C, и я не считаю, что # 1 применяется потому, что pimpl<> (C «s только члены) определяет деструктор явно.

Может кто-то пожалуйста, объясните что происходит?

хиджры

#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.ч

#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 внутренний.
  • Здесь нет B.cpp, это было бы empy.

ответ

2

Вы должны определить дескриптор C вручную после определения C :: C_impl, потому что для компилятора по умолчанию пытается сгенерировать деструктор C в точке использования, но может быть точкой, где определение C :: C_impl не может быть найдено (например, , это может быть B.cpp).

+0

Это имеет смысл, я не знал, что он был создан в точке использования. Благодаря! –

1

Я считаю, что проблема в том, что std::unique_ptr требует знания функции уничтожения (т. Е. Деструктора) во время создания экземпляра.

С деструктор по умолчанию ~C() компилятор генерирует его в точке использования, что означает, что он пытается удалить pimpl<C_impl> объект с информацией, имеющейся в его распоряжении в A.cpp. Конечно, поскольку C_impl только объявлен в этот момент, компилятор не знает, как уничтожить объект C_impl, и это ошибка, которую вы получаете.

Uncommenting ~C(); сообщает компилятору, чтобы он не беспокоился о том, как уничтожить C_impl, который будет определен где-то в другом месте. В вашем случае это определено в C.cpp, где известно определение C_impl.

В ответ на ваши изменения, деструктор pimpl<C_impl> имеет std::unique_ptr<C_impl>, который имеет не статический элемент данных с недоступным деструктором (C_impl неполный тип в точке использования жгутов A.cpp).

Смежные вопросы