2017-07-26 81 views
1

这个例子显示编译器(msvc14,gcc,clang)的奇怪行为,但是我没有找到解释。不是默认的析构函数导致不完整的类型错误

当我们实现pipml习语并使用前向声明时,我们需要考虑unique_ptr具有自己的具有不完整类型的特定行为。 这种情况被提及herehere

但是,当我们将转发类的定义移动到另一个头文件并在稍后使用客户类时在一个地方包含头文件时,编译器变得疯狂 - 在某些特殊的析构函数声明中,他们说关于不完整类型。

这是一个简单的例子。如果取消注释“#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; 
} 
+1

不确定,但我认为编译器实际上并没有抱怨析构函数,而是关于默认的构造函数,如果你声明了非默认的析构函数,则缺少 – user463035818

+0

如果你从基地包含foo.h,CASE_3编译得很好。 CPP,无论如何,你应该这样做。如果你不包含main.cpp中的foo.h,那么'CASE_1'不会编译,你不应该这样做。 – Slava

回答

1

我相信,它具有与C++标准12.4/6.

被默认并为已删除不限定的析构函数做当ODR使用的是 隐含地定义(3.2)至破坏的目的 其类别类型(3.7)或者在其第一次声明后明确默认为 。

当你有你的析构函数违约,它只会定义时使用的ODR,即当Base对象被销毁。在你的代码片段中,没有任何类型的对象会被销毁,因此程序编译 - 因为unique_ptr的deleter实际上并没有在任何地方被调用 - 它只能被Base析构函数调用,这在本场景中没有定义。

当您提供用户定义的析构函数时,它被就地定义,并且程序会变形,因为您不能破坏不完整类型的对象unique_ptr

顺便说一下,具有析构函数declared,但不是defined(如~base();)不会由于同样的原因而导致编译错误。

+0

我不确定“这种类型的物体是否被破坏”,我们有“Base b;”在main.cpp中,当它超出范围时它应该被销毁。我猜。 – degreeme

+0

@degreeme,是的,但** AFTER **中包含了'Foo'的定义。不在'Base'的头部。 – SergeyA

0

但是,当我们在一个地方与客户端类的使用移动转发类的定义,另一头文件,包括头后,编译器变得疯狂 - 在析构函数的一些特殊情况下,他们说,大约不完整的类型。

编译器都很好,Foo类定义的析构函数B定义的时候必须是可见的了。在CASE_1中发生这种情况 - 在main.cpp中定义了析构函数,并且在那里包含foo.h。CASE_2不会编译,不应该使用。 CASE_3会在您从base.cpp包括foo.h时编译,无论如何您都应该这样做,并使用此案例(并且不包括来自main.cpp的foo.h,否则您将打破pimpl习语的全部目的)。

因此,没有编译器的奇怪行为,您使用pimpl习语很奇怪,会导致您观察到的行为。

相关问题