在 class 范围内将指针传递给 constexpr 函数时是否滥用推断父模板的参数

Is it abuse to deduce parameters of parent template when passing pointer to constexpr function in the scope of a class

我得到的最小例子有点复杂:

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] 抱怨匹配 ParentKid 问题:

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:

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.

我们在我突出显示的 "otherwise" 部分。即使我们处理的是指针,class 还没有被定义为指针转换是有效的。 GCC 过于宽松。