来自a recent blog post的简化示例:
struct B { void f(); };
struct D : B { };
constexpr auto as_d = static_cast<void(D::*)()>(&D::f); // (1)
template <void (D::*)()>
struct X { };
X<as_d> x; // (2)
gcc,clang和MSVC都接受标记为as_d
的{{1}}的声明。 gcc和clang都拒绝标记为(1)
的{{1}}的声明,但MSVC接受。
gcc和clang的错误消息均表明它们知道x
是指向(2)
成员的指针。叮当声:
as_d
:错误:很抱歉,尚不支持指向不同类的成员B
的指针到成员类型<source>:9:3
的非类型模板参数
gcc:
void (D::*)()
:错误:B::f
不是类型<source>:9:7
的有效模板参数
谁是对的?如果是gcc / clang,我们违反的规则是什么?对我来说,看来void (D::*)(){((void (D::*)())B::f), 0}
是void (D::*)()
类型的转换常量表达式...
答案 0 :(得分:5)
嗯,这最终变得非常有趣。就语言而言,该程序是有效的-as_d
确实满足成为有效的非类型模板参数(它是正确类型的转换常量表达式)的要求。
但是,Itanium C++ ABI显然是does not specify a mangling,在这种情况下(也就是说,有一个非类型模板参数,其类型是从成员导出的指针,而值是一个指针) -成员到基础)。结果,针对该ABI(即clang和gcc)的编译器无法接受此代码。这解释了为什么clang的错误是“对不起,还没有”而不是“不,不好!”
另一方面,其他ABI则没有这样的问题,因此MSVC和ICC都能很好地编译程序。