Minimal example I got is a bit complicated:
struct A { };
template <int>
struct Parent { };
template <int N>
constexpr int operator*(A, Parent<N>*) { return N; }
template <class T>
using ptr = T*;
template <int>
struct Other { };
template <int N>
struct Kid: Parent<N> {
static Other<A{} * ptr<Kid>{}> o;
};
int main() {
Kid<2>{};
}
[gcc] compiles the code without any problem, [clang] complains about matching Parent
against Kid
problem:
prog.cc:7:15: note: candidate template ignored: could not match 'Parent' against 'Kid'
constexpr int operator*(A, Parent<N>*) { return N; }
To get more absurd when we change the code a bit:
struct A { };
template <int>
struct Parent { };
template <int N>
constexpr int operator*(A, Parent<N>*) { return N; }
template <class T>
using ptr = T*;
template <int>
struct Other { };
template <int N>
struct Kid: Parent<N> {
static constexpr int s = A{} * ptr<Kid>{};
};
int main() {
Other<Kid<2>::s>{};
}
[clang] also compiles the code. So... is it a bug or am I starting to go insane?
Clang is right to the letter of the law. Yes, Kid
is derived from Parent
. But that relationship can only be established once Kid
is a completely defined type. And well, [class.mem]/6:
A class is considered a completely-defined object type ([basic.types]) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, noexcept-specifiers, and default member initializers (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.
We are in the "otherwise" part I highlighted. Even though we are dealing with pointers, the class is not yet considered defined for the pointer conversion to be valid. GCC is overly permissive.