C ++では、不変性を捨てるという概念は、標準によって厳密に定義されています。static_cast
およびなどのreinterpret_cast
キャストは、明示的な変換で定数をキャストすることはできません。恒常性を捨てる定義は、資格変換の定義に大きく依存しています。これは、2つのタイプが同様のタイプまでの資格変換を実行できることを示しています。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コンポーネントが同じであるか、1つが「Niの配列」でもう1つが「未知の境界の配列」である
最初のケースでは、配列とポインターは類似していません。GCCとclangは、型が類似X[]
しX*
ていないことを正しく示しているreinterpret_cast
ため、配列を超えたconst修飾子を無視するようにconst int* const
なります(したがって、に変換される可能性がありますint*
)。すべてが良いです。
2番目のケースでは、ポインターとメンバーポインターは類似してはなりません。clangは、型が類似していないと正しく言っているのでreinterpret_cast
、キックインします。GCCは、型が似ていると言っているので、constnessを捨てています。ここではGCCが間違っていると確信しています。
3番目のケースでは、異なるクラスの2つのメンバーポインターを比較しています。clangとGCCはどちらも類似していると考えていますが、ドラフトを注意深く見ると、「タイプのクラスCiのメンバーへのポインター」と書かれています。それらが異なるタイプである場合、それらは類似していないと見なすべきではありませんか?ここでclangとgccの両方が間違っていますか?
ちなみに、MSVCは警告なしで上記のすべてを許可します(godboltのx64 msvc 19.27)。
編集:3番目のケースはより簡単な方法で再現できます:
const int T::* f;
reinterpret_cast<int F::*>(f);
私はRichardSmith(clangの作成者)と話し合いましたが、パスする-pedantic
と3つのケースすべてがコンパイルに失敗するはずであり、MSVCが実際にエラーのあるケースであるという結論に達しました。これは、reinterpret_cast <>がconstnessをキャストする各例に単一のcv-decompositionが存在するため、操作が理想的には機能しないはずであるためです。ただし、clangには、これが警告とともに機能することを可能にする拡張機能があります。それ自体はUBではありません。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加