Why an incomplete type is detected in clang inside

2019-02-25 07:10发布

问题:

Today, I encountered a compile issue in clang that surprised me. I guess is reasonable but I like to dig deeper and hear more details. Some standard references if possible also.

I have a class with a template method which rely on a member which his type is undefined in the header (but not in the source). Something like the following:

// 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) );
}

In VisualStudio, it compiles. I expect this to only complain when instanciating; so inlining. However, in clang, this doesn't compile as soon as someone includes the header. Forcing me to include Page.h in Menu.h (which I want to avoid at all cost).

Like:

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

even if the whole Another.cpp is not using VisitWidget (even in other headers)

I guess that this is caused by inline somehow, since the compiler is not obligated to really use it, but since there are templates in the middle I am not so sure. Is really clang checking the type?

回答1:

Yes, this compiles in MSVC because it has a well-known bug. It doesn't implement two-step template instantiation.

To elaborate. MSVC wrongly defers template parsing until it is actually instantiated in the code. Likely it happens after full Page definition becomes visible.

However, standard demands that the template is pre-parsed at the point of definition, and all types which are not dependent on template arguments are resolved. This fails, since m_page is not dependent on visitor argument - and it is still incomplete type at this point.

P.S. I can't even express how outraged I am at MSFT for this blatant Standard violation (among others). It makes cross-platform development a real pain, when MS-compliant code has to be ported to conforming compilers.