【问题标题】:Is clang wrong, gcc wrong, or both wrong in this case - casting away constness with member pointers在这种情况下是 clang 错误、gcc 错误还是两者都错误 - 用成员指针抛弃 constness
【发布时间】:2020-10-14 21:33:18
【问题描述】:

在 C++ 中,casting away constness 的概念由标准严格定义。诸如static_castreinterpret_cast 之类的强制转换不允许在显式转换中丢弃常量。抛弃 constness 的定义在很大程度上依赖于限定转换定义,它表示两种类型可以执行到 similar 类型的限定转换。例如,reinterpret_cast 可以对不相似的部分进行强制转换。

这是godbolt 中也提供的示例:

struct T{};
struct F{};

void f() {
    const int* const T::* const * const * const x {};
    
    // pointer to array - works in clang (trunk),
    // works in gcc (trunk)
    reinterpret_cast<int* const T::* const (*) [2]>(x);

    // member pointer to pointer - works in clang (trunk), 
    // fails in gcc (trunk)
    reinterpret_cast<int* const * const * const * const>(x);

    // member pointer to another member pointer type - fails in clang (trunk),
    // fails in gcc (trunk)
    reinterpret_cast<int* const F::* const * const * const>(x);
}

回忆一下类似的定义。 每个级别的类型必须是类别

...每个 Pi 是“指向”([dcl.ptr])、“指向类型 Ci 的成员的指针”([dcl.mptr])、“Ni 数组”或“未知数组”边界” ([dcl.array])

相似,每个Pi必须属于同一类别:

...对应的 Pi 分量要么相同,要么一个是“Ni 的数组”,另一个是“未知边界的数组”

在第一种情况下,数组和指针并不相似。 GCC 和 clang 正确地表示 X[]X* 类型不相似,因此 reinterpret_cast 开始忽略任何经过数组的 const 限定符(因此 const int* const 可以转换为 int*)。一切都很好。

在第二种情况下,指针和成员指针不应该相似。 clang 正确地表示类型不相似,因此 reinterpret_cast 起作用。GCC 表示类型相似,因此我们正在抛弃 constness我很确定 GCC 在这里是错误的。

在第三种情况下,我们比较不同类的两个成员指针。 clang 和 GCC 都认为它们是相似的,但是如果我们仔细查看草案,它会说 “指向类型 Ci 的成员的指针”。如果它们是不同的类型,我们不应该认为它们不相似吗? clang 和 gcc 都错了吗?

顺便说一句,MSVC 允许上述所有操作,而不会发出警告(godbolt 上的 x64 msvc 19.27)。

编辑:第三种情况可以用更简单的方式重现:

const int T::* f;
reinterpret_cast<int F::*>(f);

【问题讨论】:

  • 代码会产生很多警告。我猜你在某个地方有 UB。
  • 您确定您在标准行为实际定义的领域中坚定不移吗? ??????
  • @yeoman 我不是,但这就是我添加语言律师标签的原因:)
  • “抛弃常量”的定义并不关心相似性。注意 Ps 上的上标。

标签: c++ gcc casting clang language-lawyer


【解决方案1】:

I had a discussion with Richard Smith (the creator of clang) 和我得出的结论是,如果我们传入-pedantic,那么所有三种情况都应该无法编译,并且 MSVC 确实是出现错误的情况。这是因为在每个示例中都存在一个 cv 分解,其中 reinterpret_cast 将抛弃 constness,因此理想情况下该操作不应该能够工作。但是,clang 具有允许它处理警告的扩展。它本身不是 UB。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    • 2017-05-26
    • 2020-06-17
    • 2011-05-21
    • 2021-08-15
    • 2012-04-08
    • 1970-01-01
    相关资源
    最近更新 更多