我得到的最小示例有点复杂:
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 :
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.
我们处于我强调的“否则”部分。即使我们正在处理指针,该类仍未被视为已定义,因此指针转换是有效的。 GCC 过于宽松。
关于c++ - 在类范围内将指针传递给 constexpr 函数时是否滥用推断父模板的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47854760/