const_cast
はポインタと参照で機能することを理解しています。
const_cast
への入力は、ポインターまたは参照であると想定しています。入力がconst int
へのポインター/参照である場合、なぜconstnessが削除されないのか知りたい
次のコードは期待どおりに動作します。
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
への参照を試しても、値が変化していないようです。
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
}
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
への入力はポインタ/参照であると考えてください。
この背後にある哲学を理解するのを手伝ってください。ありがとう。
Constnessには2種類あります。
objectの一貫性は、オブジェクトの固有のプロパティです。変更できません。
印刷された本のページを考えてください。文字列として表示でき、変更できません。それはそれが言うことを言い、それだけです。だからconst string
。
黒板について考えてみましょう。何か書いてあるかもしれません。あなたはそれを拭いて、何か他のものを書くことができます。したがって、黒板は非定数string
です。
もう1つの種類の定数は、ポインターと参照の定数です。この一定性は、ポイントされたオブジェクトの固有のプロパティではなく、許可です。 許可でオブジェクトを変更できないこのポインタを介してでないと表示されます。オブジェクト自体を変更できるかどうかについては何も述べていません。
したがって、constポインターがある場合、必ずしもそれが実際に何を指しているのかはわかりません。多分それは本のページです。多分それは黒板です。ポインタはわかりません。
さて、それが実際に黒板であることをどういうわけか知っているなら、あなたは厄介で、先に進んでそれに書かれていることを変更する許可を要求することができます。それがconst_castが行うことです。それはあなたに何かをする許可を与えます。
文字列を変更する許可を要求し、それが印刷されたページであることが判明した場合はどうなりますか?あなたはあなたの許可を得て、あなたは先に進んでそれを拭きます...そして...正確に何が起こるかはndefinedです。おそらく何もないでしょう。おそらく、プリントが汚れており、元の文字列を認識したり、上に何かを書き込んだりすることはできません。おそらくあなたの世界は小さな破片に爆発します。試してみることができますが、同じことが明日起こるとは限りません。
(2)と(3)は同じ原理なので、(2)についてのみ説明します。
この線
const_cast<int&>(ri) = 321;
未定義の動作があります。
const
オブジェクトは、const_cast
を使用した場合でも、標準に従って変更することはできません。ポインター/参照からconst
を削除し、参照先/参照先オブジェクトを変更する場合、参照先/参照先オブジェクトを最初にconst
として宣言してはなりません。
const_cast
は、何らかの理由で何かへのconstポインターがあり、何かがconst
として宣言されていないことがわかっている場合にのみ使用してください。
const_cast
による定数の変更は、未定義の動作です。
コンパイラーは、定数変数を出力しようとしていることを認識し、変更できないことを知っているため、コンパイルします。
cout << "i: " << i << endl;
に:
cout << "i: " << 123 << endl;
参照: https://godbolt.org/z/bYb0mx 。最適化が有効になっていると、コードを最適化して123だけを出力します: https://godbolt.org/z/4Ttlmj 。
結局、コンパイラはより高速/より小さなコードを作成するために仮定を立てます。未定義の動作の領域に入ると、それらの仮定の一部が不正確になり、予期しない結果が生じる可能性があります。