c++ - 指向数据成员转换的 Constexpr 指针

标签 c++ language-lawyer constexpr pointer-to-member

GCC 8.2.1 和 MSVC 19.20 编译以下代码,但 Clang 8.0.0 和 ICC 19.0.1 无法编译。

// Base class.
struct Base {};

// Data class.
struct Data { int foo; };

// Derived class.
struct Derived : Base, Data { int bar; };

// Main function.
int main()
{
  constexpr int Data::* data_p{ &Data::foo };
  constexpr int Derived::* derived_p{ data_p };
  constexpr int Base::* base_p{ static_cast<int Base::*>(derived_p) };

  return (base_p == nullptr);
}

Clang 8.0.0 的错误信息如下:

case.cpp:16:33: error: constexpr variable 'base_p' must be initialized by a constant expression
  constexpr int Base::* base_p{ static_cast<int Base::*>(derived_p) };
                              ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

我注意到它在两种情况下可以很好地使用 Clang 进行编译:

  • 从最后一个定义中删除constexpr
  • 将行 constexpr int Derived::* derived_p{ data_p }; 替换为 constexpr int Derived::* derived_p{ &Derived::bar };

constexpr 表达式(使 Clang 和 ICC 失败的表达式)应该编译吗?

最佳答案

我相信 GCC 和 MSVC 是正确的,这段代码应该可以编译。

data_p指向Data的成员fooderived_p 通过指向成员转换的隐式指针指向Data 基类子对象的成员foo [conv.mem]/2 .

来自 [expr.static.cast]/12

A prvalue of type “pointer to member of D of type cv1 T” can be converted to a prvalue of type “pointer to member of B of type cv2 T”, where B is a base class of D, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. […] If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the behavior is undefined. [ Note: Although class B need not contain the original member, the dynamic type of the object with which indirection through the pointer to member is performed must contain the original member; see [expr.mptr.oper].  — end note ]

正如@geza 在他下面的评论中所指出的,类 BaseDerived 的基类,后者包含原始成员 Data::foo 在其 Data 基类子对象中(上面引用中的注释似乎是支持这种解释的进一步证据)。因此,用于初始化 base_pstatic_cast 格式正确并且具有明确定义的行为。从 Base 基类子对象的角度来看,结果指针指向 Derived 对象的 Data::foo 成员派生对象。

要初始化一个 constexpr 对象,需要一个常量表达式 [dcl.constexpr]/9 。我们的表达式(static_cast 的结果)是一个核心常量表达式,因为 [expr.const]/2 中没有任何其他内容。它也是一个常量表达式,因为它是满足 [expr.const]/5 中规定的所有约束的纯右值。

关于c++ - 指向数据成员转换的 Constexpr 指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55844284/

相关文章:

c++ - 使用 C++ 获取 vector<cv::Vec3b> 中出现次数最多的值

c++ - 如何从 vector C++ 中的字符串指向第 n 个字符

multithreading - Stroustrup指的是std::async的什么限制?

C++ 17 元编程递归结构 : enum or constexpr

c++ - 在 Vector 中查找值的索引,最接近输入

c++ - 用户定义的类序列化、C++ 和 msgpack

c++ - 非类型模板参数中的占位符类型是否可以涉及作为模板参数传递的函数的重载解析?

css - 在CSS Flexbox中,为什么没有“justify-items”和“justify-self”属性?

c++ - 无法编译包含 "if constexpr"的函数模板实例化

c++ - 通过重新解释转换将 constexpr 值转换为指针