В этом примере показано странное поведение компиляторов (msvc14, gcc, clang), но объяснения я не нашел.
Когда мы реализуем идиому pipml и используем предварительное объявление, нам нужно учитывать, что unique_ptr имеет собственное специфическое поведение с неполными типами. Эти случаи упоминались здесь и здесь.
Но когда мы переносим определение пересылаемого класса в другой заголовочный файл и включаем заголовки в одном месте позже с использованием клиентского класса, компиляторы сходят с ума - в некоторых частных случаях объявления деструктора они говорят о неполном типе.
Вот минимальный пример. Если раскомментировать "#define CASE_2" или "#define CASE_3" и попытаться собрать его, будет ошибка компиляции.
файл foo.h
#ifndef FOO_H
#define FOO_H
class Foo{};
#endif // FOO_H
файл base.h
#ifndef BASE_H
#define BASE_H
#include <memory>
//#define CASE_1
//#define CASE_2
//#define CASE_3
class Foo;
class Base
{
public:
#if defined(CASE_1)
~Base() = default; // OK!
#elif defined(CASE_2)
~Base() {}; // error: invalid application of 'sizeof' to incomplete type 'Foo'
#elif defined(CASE_3)
~Base(); // error: invalid application of 'sizeof' to incomplete type 'Foo'
#endif
// OK!
private:
std::unique_ptr<Foo> m_foo;
};
#endif // BASE_H
файл base.cpp
#include "base.h"
#if defined(CASE_3)
Base::~Base()
{
}
#endif
файл main.cpp
#include "foo.h" // No matter order of this includes
#include "base.h" //
int main()
{
Base b;
}
CASE_3
прекрасно компилируется, если вы включаете foo.h из base.cpp, что вы и так предполагаете делать. ИCASE_1
не компилируется, если вы не включаете foo.h из main.cpp, чего делать не следует. - person Slava   schedule 26.07.2017