我得到的最小例子有点复杂:
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]编译代码没有任何问题,[clang]抱怨Parent
匹配Kid
问题:
prog.cc:7:15: note: candidate template ignored: could not match 'Parent' against 'Kid'
constexpr int operator*(A, Parent<N>*) { return N; }
当我们稍微更改代码时更加荒谬:
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]也编译代码。所以...这是一个错误还是我开始变得疯狂?
Clang对法律条文是正确的。是的,Kid
来自Parent
。但只有在Kid
是完全定义的类型时才能建立这种关系。好吧,[class.mem]/6:
在类说明符的结束时,类被认为是完全定义的对象类型([basic.types])(或完整类型)。在类成员规范中,该类在函数体,默认参数,noexcept-specifiers和默认成员初始化器(包括嵌套类中的这类事物)中被视为完整。否则,它在其自己的类成员规范中被视为不完整。
我们强调了“其他”部分。即使我们处理指针,该类还没有被认为是指针转换有效。海湾合作委员会过于宽容。