将基础成员指针转换为派生成员指针

时间:2018-08-19 14:04:39

标签: c++ language-lawyer c++17

来自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::*)()类型的转换常量表达式...

1 个答案:

答案 0 :(得分:5)

嗯,这最终变得非常有趣。就语言而言,该程序是有效的-as_d确实满足成为有效的非类型模板参数(它是正确类型的转换常量表达式)的要求。

但是,Itanium C++ ABI显然是does not specify a mangling,在这种情况下(也就是说,有一个非类型模板参数,其类型是从成员导出的指针,而值是一个指针) -成员到基础)。结果,针对该ABI(即clang和gcc)的编译器无法接受此代码。这解释了为什么clang的错误是“对不起,还没有”而不是“不,不好!”

另一方面,其他ABI则没有这样的问题,因此MSVC和ICC都能很好地编译程序。