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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With