This seems to be a gcc bug. Here's the MCVE (4 lines):
struct X; template<int> struct A { friend constexpr A f(X*) { return {}; } };
This is accepted by clang and MSVC.
As you noticed, gcc accepts the same program without constexpr , or if you explicitly create an instance of A<0> (for example, from template struct A<0>; ) to auto&& a = f((X*)0); . This suggests that the gcc issue has an implicit template instance [temp.inst] :
1 - If the specification of the class template has not been explicitly created (14.7.2) or explicitly specialized (14.7.3), the specialization of the class template is implicitly created when the specialization is referenced in a context that requires a fully defined type of object or when the completeness of the class type affects semantics programs.
A class template A<0> is required in the expression return constexpr A<0> f(X*) , so it must be implicitly created at this point. Although the definition of a friend's function is lexical in class A , the class should not be considered incomplete in the definition of a friend's function; For example, the following universal program:
struct Y; struct B { friend constexpr B f(Y*) { return {}; } }; struct Y { friend constexpr B f(Y*); }; auto&& b = f((Y*)0);
Interestingly, both gcc and clang (although not MSVC) have problems with the following program (again, fixed by removing constexpr or an explicitly created instance using template struct C<0>; ):
struct Z; template<int> struct C { friend constexpr C* f(Z*) { return 0; } }; struct Z { friend constexpr C<0>* f(Z*); };
I would suggest using an explicit instance workaround. In your case, it will be:
template class Options_proxy<File_options_impl>;
source share