今天,我在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 中,一旦有人包含标头,它就不会立即编译。强迫我将Page.h包含在Menu.h中(我想不惜一切代价避免)。
喜欢:
// Another.cpp (not Menu.cpp)
#include "Menu.h" // this trigger and error due Page is an incomplete type
即使整个 Other.cpp 没有使用 VisitWidget(即使在其他标头中)
我想这是由内联引起的,因为编译器没有义务真正使用它,但由于中间有模板,我不太确定。真的是叮当检查类型吗?
是的,这是在 MSVC 中编译的,因为它有一个众所周知的错误。它不实现两步模板实例化。
详细阐述。MSVC 错误地延迟模板分析,直到它在代码中实际实例化。很可能在完整的Page
定义变得可见之后发生。
但是,标准要求在定义点对模板进行预解析,并且解析所有不依赖于模板参数的类型。这失败了,因为m_page
不依赖于访问者参数 - 并且此时它仍然是不完整的类型。
附言我什至无法表达我对MSFT对这种公然违反标准的行为(以及其他)的愤怒。当必须将符合 MS 的代码移植到符合标准的编译器时,它使跨平台开发变得非常痛苦。