web-dev-qa-db-ja.com

const_castがconstへのポインターではなく、ポインターのconstnessを削除するのはなぜですか?

const_castはポインタと参照で機能することを理解しています。

const_castへの入力は、ポインターまたは参照であると想定しています。入力がconst intへのポインター/参照である場合、なぜconstnessが削除されないのか知りたい

次のコードは期待どおりに動作します。

  1. const_cast with multilevel pointers

    int main()
    {
        using std::cout;
        #define endl '\n'
        const int * ip = new int(123);
        const int * ptr = ip;
        *const_cast<int*>(ptr) = 321;
        cout << "*ip: " << *ip << endl;  // value of *ip is changed to 321
    }
    

    しかし、const intへのポインターまたはconst intへの参照を試しても、値が変化していないようです。

  2. const_cast const intを参照

    int main()
    {
        using std::cout;
        #define endl '\n'
        const int i = 123;
        const int & ri = i;
        const_cast<int&>(ri) = 321;
        cout << "i: " << i << endl;  // value in 'i' is 123
    }
    
  3. const_cast with const intへのポインタ

    int main()
    {
        using std::cout;
        #define endl '\n'
        const int i = 123;
        const int * ri = &i;
        *const_cast<int*>(ri) = 321;
        cout << "i: " << i << endl;  // value in 'i' is 123
    }
    

(1)は期待どおりに動作しますが、なぜ(2)(3)が動作しないのか理解できませんconst_castへの入力はポインタ/参照であると考えてください。

この背後にある哲学を理解するのを手伝ってください。ありがとう。

14
Rahul

Constnessには2種類あります。

objectの一貫性は、オブジェクトの固有のプロパティです。変更できません。

印刷された本のページを考えてください。文字列として表示でき、変更できません。それはそれが言うことを言い、それだけです。だからconst string

黒板について考えてみましょう。何か書いてあるかもしれません。あなたはそれを拭いて、何か他のものを書くことができます。したがって、黒板は非定数stringです。

もう1つの種類の定数は、ポインターと参照の定数です。この一定性は、ポイントされたオブジェクトの固有のプロパティではなく、許可です。 許可でオブジェクトを変更できないこのポインタを介してでないと表示されます。オブジェクト自体を変更できるかどうかについては何も述べていません。

したがって、constポインターがある場合、必ずしもそれが実際に何を指しているのかはわかりません。多分それは本のページです。多分それは黒板です。ポインタはわかりません。

さて、それが実際に黒板であることをどういうわけか知っているなら、あなたは厄介で、先に進んでそれに書かれていることを変更する許可を要求することができます。それがconst_castが行うことです。それはあなたに何かをする許可を与えます。

文字列を変更する許可を要求し、それが印刷されたページであることが判明した場合はどうなりますか?あなたはあなたの許可を得て、あなたは先に進んでそれを拭きます...そして...正確に何が起こるかはndefinedです。おそらく何もないでしょう。おそらく、プリントが汚れており、元の文字列を認識したり、上に何かを書き込んだりすることはできません。おそらくあなたの世界は小さな破片に爆発します。試してみることができますが、同じことが明日起こるとは限りません。

21

(2)と(3)は同じ原理なので、(2)についてのみ説明します。

この線

const_cast<int&>(ri) = 321;

未定義の動作があります。

constオブジェクトは、const_castを使用した場合でも、標準に従って変更することはできません。ポインター/参照からconstを削除し、参照先/参照先オブジェクトを変更する場合、参照先/参照先オブジェクトを最初にconstとして宣言してはなりません。

const_castは、何らかの理由で何かへのconstポインターがあり、何かがconstとして宣言されていないことがわかっている場合にのみ使用してください。

14
geza

const_castによる定数の変更は、未定義の動作です。

コンパイラーは、定数変数を出力しようとしていることを認識し、変更できないことを知っているため、コンパイルします。

cout << "i: " << i << endl;

に:

cout << "i: " << 123 << endl;

参照: https://godbolt.org/z/bYb0mx 。最適化が有効になっていると、コードを最適化して123だけを出力します: https://godbolt.org/z/4Ttlmj

結局、コンパイラはより高速/より小さなコードを作成するために仮定を立てます。未定義の動作の領域に入ると、それらの仮定の一部が不正確になり、予期しない結果が生じる可能性があります。

4
Alan Birtles