为什么在模板方法中的clang中检测到不完整的类型?

时间:2016-06-02 18:46:24

标签: c++ templates c++11 clang smart-pointers

今天,我在clang中遇到了一个让我感到惊讶的编译问题。我想是合理的,但我喜欢深入挖掘并听到更多细节。如果可能,还有一些标准参考。

我有一个带有模板方法的类,它依赖于一个成员,该成员在标题中未定义其类型(但不在源代码中)。如下所示:

// Menu.h
class Page;

class Menu
{
public:
    .... // stuff

template<class Visitor>
void VisitWidget( Visitor&& visitor);

private:
std::unique_ptr<Page> m_page; // destructor implemented in source file, so Page is an incomplete type
};

template<class Visitor>
inline void Menu::VisitWidget( Visitor&& visitor);
{
    m_page->Visit( std::forward<Visitor>(visitor) );
}

在VisualStudio中,它编译。我希望这只会在实施时抱怨;如此内联。但是,在clang中,只要有人包含标题,就不会编译。强迫我在Menu.h中包含Page.h(我想不惜一切代价避免使用)。

像:

// Another.cpp (not Menu.cpp)
#include "Menu.h" // this trigger and error due Page is an incomplete type

即使整个Another.cpp没有使用VisitWidget(即使在其他标题中)

我想这是内联引起的,因为编译器没有义务真正使用它,但由于中间有模板我不太确定。是真的铿锵检查类型?

1 个答案:

答案 0 :(得分:8)

是的,这在MSVC中编译,因为它有一个众所周知的bug。它没有实现两步模板实例化。

详细说明。 MSVC错误地推迟了模板解析,直到它在代码中实际实例化为止。可能会在完整Page定义变为可见之后发生。

但是,标准要求在定义点预解析模板,并解析所有不依赖于模板参数的类型。这失败了,因为m_page不依赖于访问者参数 - 此时它仍然是不完整的类型。

P.S。我甚至无法表达我对MSFT这种明显标准违规行为的愤怒(以及其他)。当符合MS的代码必须移植到符合编译器的时候,它使跨平台开发变得非常痛苦。