スタンダードC++

Const correctness

“const correctness”とは何ですか?

いいことだ。 これは、キーワードconstを使用して、constオブジェクトが変更されないようにすることを意味します。

たとえば、std::stringを受け入れる関数f()を作成し、f()に渡される呼び出し元のstd::stringを変更することをcallersnotに約束したい場合は、f()std::stringパラメータを受け取ることがで…

  • void f1(const std::string& s); // 参照渡し-へ-const
  • void f2(const std::string* sptr); // ポインタで渡す-に-const
  • void f3(std::string s); // 値渡し

参照渡しconstおよびポインタ渡しconstの場合、f()関数内で呼び出し元のstd::stringを変更しようとすると、コンパイラによってエラーとしてフラグが付けられ コンパイル時。 このチェックはコンパイル時に完全に実行されます:constの実行時スペースや速度コストはありません。 値渡しの場合(f3())、呼び出された関数は呼び出し元のstd::stringのコピーを取得します。 これは、f3()がlocalcopyを変更できることを意味しますが、f3()が戻るとコピーは破棄されます。 特にf3()は呼び出し元のstd::stringオブジェクトを変更できません。逆の例として、std::stringを受け入れる関数g()を作成したいが、g()が呼び出し元のstd::stringオブジェクトを変更する可能性があることをcallersに知らせたいとします。 この場合、g()std::stringパラメータを受け取ることができます…

  • void g1(std::string& s); // 参照から非へのパス-const
  • void g2(std::string* sptr); // ポインタから非へのパス-const

これらの関数にconstがないことは、コンパイラに、コンパイラのstd::stringオブジェクトを変更することが許可されている(ただし必須ではない)ことを伝えます。 したがって、彼らはstd::stringf()関数のいずれかに渡すことができますが、f3()(そのパラメータを”値で”受け取るもの)だけがstd::stringg1()またはg2()に渡すことができます。 f1()またはf2()g()関数のいずれかを呼び出す必要がある場合、std::stringオブジェクトのローカルコピーをg()関数に渡す必要があります。f1()またはf2()へのパラメータをg()関数に直接渡すことはできません。 E.g.,

void g1(std::string& s);void f1(const std::string& s){ g1(s); // Compile-time Error since s is const std::string localCopy = s; g1(localCopy); // Okay since localCopy is not const}

当然のことながら、上記の場合、g1()が行う変更は、f1()にローカルなlocalCopyオブジェクトに行われます。f1()を参照して渡されたconstパラメータには変更は行われません。f1()を参照して渡されたconstパラメータには変更は行われません。

“const correctness”は通常の型安全性とどのように関連していますか?

パラメータのconst-nessを宣言することは、型安全性の単なる別の形式です。

通常の型安全性がシステムを正しくするのに役立つことがわかった場合(特に大規模なシステムでは)、const正しさも役立ちます。

const正しさの利点は、意図していなかった何かを誤って変更することを防ぐことです変更される可能性があります。 コンパイラや他のプログラマに重要な意味情報のいくつかの追加の部分を伝えているthebenefitで、いくつかの余分なキーストローク(constキーワード)でコードを飾る必要

概念的には、const std::stringは通常のstd::stringとは異なるクラスであると想像することができます。constバリアントは概念的には非constバリアントで利用可能なさまざまな変 たとえば、概念的には、const std::stringには代入演算子+=やその他の突然変異演算がないと想像できます。

const正しいものを”すぐに”または”後で”取得しようとする必要がありますか?

非常に、非常に、非常に初めに。

バックパッチconst正しさは雪だるま効果をもたらします:”ここに”を追加するすべてのconstは、”あそこに”を追加する必要があります。”

constを早く頻繁に追加します。

“const X*p”とはどういう意味ですか?

pXクラスのオブジェクトを指していることを意味しますが、pXオブジェクトを変更するために使用できません(当然pNULLも使用できます)。

右から左に読む:”pは定数であるXへのポインタです。たとえば、クラスXinspect() constのようなconstメンバー関数がある場合、p->inspect()と言っても大丈夫です。 しかし、クラスXmutate()という非constメンバ関数がある場合、p->mutate()と言うとエラーになります。

重要なことに、このエラーはコンパイル時にコンパイラによってキャッチされます-ランタイムテストは行われません。 つまり、constはあなたのプログラムを遅くせず、実行時に物事をチェックするために余分なテストケースを書く必要はありません-thecompilerはコンパイル時に作業

“const X*p”、”X*const p”、”const X*const p”の違いは何ですか?

ポインタ宣言を右から左に読み込みます。

  • const X* ppXを指しているconst“を意味します。Xオブジェクトはpを介して変更することはできません。
  • X* const pは、”pconst以外のXへのポインタである”ことを意味します。const: ポインタp自体を変更することはできませんが、pを介してXオブジェクトを変更することはできます。
  • const X* const pは、”pconstであるXへのconstポインタである”ことを意味します。p自体を変更することも、pを介してXオブジェクトを変更することもできません。pを介してXオブジェクトを変更することもできません。

そして、ああ、いや、私はあなたのポインタ宣言を右から左に読むことに言及しましたか?

“const X&x”とはどういう意味ですか?

xXオブジェクトのエイリアスを意味しますが、xを介してそのXオブジェクトを変更することはできません。

右から左に読む: “xX、つまりconstへの参照です。たとえば、クラスXinspect() constのようなconstメンバー関数がある場合、x.inspect()と言っても大丈夫です。 しかし、クラスXmutate()という非constメンバー関数がある場合、x.mutate()と言うとエラーになります。

これは、コンパイラがコンパイル時にすべてのチェックを行うという事実を含め、constへのポインタと完全に対称的です。constはプログラムを遅くせず、実行時に物事をチェックするために余分なテストケースを書く必要はありません。”X const&x”と”X const*p”はどういう意味ですか?

X const& xconst X& xに相当し、X const* xconst X* xに相当します。

一部の人々は、”一貫性のあるconst“と呼んだり、Simon Brandによって造語された用語を使用して、”Eastconst“と呼んだりして、正しいスタイルを好む人もいます。”確かに、”Eastconst“スタイルは代替よりも一貫性があります:”Eastconst“スタイルは常にそれが構成するものの右側にconstを置きますが、他のスタイルは時々constを左に置き、時には右に置くことがあります(constポインタ宣言とconstメンバ関数の場合)。

“Eastconst“スタイルでは、constであるローカル変数が右のconstで定義されます:int const a = 42;。 同様に、constであるstatic変数はstatic double const x = 3.14;として定義されます。基本的にすべてのconstは、右にある必要があるconstを含む、それが構築するものの右側に終わります:constポインタ宣言とconstメンバー関数。

“Eastconst“スタイルも型エイリアスと一緒に使用すると混乱が少なくなります。foobarはなぜここで異なる型を持っていますか?

using X_ptr = X*;const X_ptr foo;const X* bar;

“Eastconst“スタイルを使用すると、これがより明確になります:

using X_ptr = X*;X_ptr const foo;X* const foobar;X const* bar;

ここでは、foofoobarが同じ型であり、barが異なる型であることがより明確です。

“Eastconst“スタイルもポインタ宣言とより一貫しています。 伝統的なスタイルを対比:

const X** foo;const X* const* bar;const X* const* const baz;

“Eastconst“スタイル

X const** foo;X const* const* bar;X const* const* const baz;

これらの利点にもかかわらず、const-on-the-rightスタイルはまだ普及していないため、レガシーコードは伝統的なスタイルを持つ傾向があります。

“X&const x”は意味がありますか?

いいえ、それはナンセンスです。

上記の宣言が何を意味するのかを知るには、右から左に読んでください: “xconstへの参照ですX“。 しかし、それは冗長です—参照は常にconst、あなたはそれが別のオブジェクトを参照させるためにareferenceを再シートすることはできませんという意味で。 決して。 constの有無にかかわらず。つまり、”X& const x“は”X& x“と機能的に同等です。 それは人々を混乱させるでしょう—constは、あなたが”const X& x“と言ったかのように、Xconstであると思う人もいます。

“constメンバー関数”とは何ですか?

そのオブジェクトを検査する(変更するのではなく)メンバー関数。

constメンバー関数は、メンバー関数のパラメーターリストの直後にconstサフィックスで示されます。 サフィックスconstを持つMemberfunctionsは、”constメンバー関数”または”インスペクタ”と呼ばれます。”constサフィックスのないメンバー関数は、”非constメンバー関数”または”ミューテータ”と呼ばれます。”

class Fred {public: void inspect() const; // This member promises NOT to change *this void mutate(); // This member function might change *this};void userCode(Fred& changeable, const Fred& unchangeable){ changeable.inspect(); // Okay: doesn't change a changeable object changeable.mutate(); // Okay: changes a changeable object unchangeable.inspect(); // Okay: doesn't change an unchangeable object unchangeable.mutate(); // ERROR: attempt to change unchangeable object}

unchangeable.mutate()を呼び出そうとすると、コンパイル時にエラーが発生します。 constには実行時スペースやspeedpenaltyはなく、実行時にテストケースを記述してチェックする必要はありません。

inspect()メンバー関数の末尾のconstは、メソッドがオブジェクトのabstract(client-visible)状態を変更しないことを意味するために使用する必要があります。 これは、メソッドがtheobjectのstructの”生のビット”を変更しないということとは少し異なります。 C++コンパイラは、通常は解決できないaliasing問題を解決できない限り、”ビットごとの”解釈を取ることはできません(つまり、オブジェクトの状態を変更する このエイリアシングの問題からの別の(重要な)洞察: pointer-to-constを持つオブジェクトを指すことは、オブジェクトが変更されないことを保証するものではありません。

参照ごとの戻り値とconstメンバー関数の関係は何ですか?

インスペクタメソッドからの参照によってthisオブジェクトのメンバーを返す場合は、constへの参照(const X& inspect() const)または値(X inspect() const)を使用して返す必要があります。

class Person {public: const std::string& name_good() const; // Right: the caller can't change the Person's name std::string& name_evil() const; // Wrong: the caller can change the Person's name int age() const; // Also right: the caller can't change the Person's age // ...};void myCode(const Person& p) // myCode() promises not to change the Person object...{ p.name_evil() = "Igor"; // But myCode() changed it anyway!!}

良いニュースは、あなたがこれを間違って取得した場合、コンパイラが頻繁にあなたをキャッチするということです。 特に、上記のPerson::name_evil()のように、thisオブジェクトのメンバーをconst以外の参照で誤って返すと、コンパイラはそれを検出し、この場合はPerson::name_evil()の内部をコンパイル中にコンパイ

悪いニュースは、コンパイラが常にあなたを捕まえるとは限らないということです。

翻訳:あなたは考える必要があります。 それはあなたを怖がらせる場合は、仕事の別の行を見つけます。

このセクション全体に広がる”const哲学”を覚えておいてください:constメンバー関数は、thisオブジェクトの論理状態(別名abstract状態別名meaningwisestate)を変更しない(または呼び出し元 オブジェクトが内部的にどのように実装されているのかではなく、オブジェクトの意味を考えてみてください。 人の年齢と名前は論理的にその人の一部ですが、その人の隣人と雇用主はそうではありません。 thisオブジェクトの論理的/抽象的/意味的状態の一部を返すインスペクタメソッドは、その部分がthisオブジェクト内に物理的に埋め込まれた直接データメン

“const-overloading”の取引は何ですか?

constオーバーロードはconstの正しさを達成するのに役立ちます。

constオーバーロードは、インスペクタメソッドとミューテータメソッドが同じ名前で、同じ数のパラメータとタイプのパラメータを持つ場合です。 二つの異なるメソッドは、inspectorがconstであり、mutatorが非constであることだけが異なります。

constオーバーロードの最も一般的な使用法は、添字演算子を使用することです。 通常、std::vectorなどの標準コンテナテンプレートのいずれかを使用する必要がありますが、subscriptoperatorを持つ独自のクラスを作成する必要がある場合は、経験則があります。

class Fred { /*...*/ };class MyFredList {public: const Fred& operator (unsigned index) const; // Subscript operators often come in pairs Fred& operator (unsigned index); // Subscript operators often come in pairs // ...};

const添字演算子はconst参照を返すため、コンパイラは呼び出し元がFredを誤って変更/変更するのを防ぎます。 非const添字演算子は、呼び出し元(およびコンパイラ)がFredオブジェクトを変更することを許可されていることを伝える方法である、非const参照を返します。

MyFredListクラスのユーザーが添字演算子を呼び出すと、コンパイラはMyFredListの定数に基づいて呼び出すオーバーロードを選択します。 呼び出し元がMyFredList aまたはMyFredList& aを持っている場合、aconst以外の添字演算子を呼び出し、呼び出し元はconst以外の添字演算子への参照になります。Fred:

たとえば、class Fredにinspector-methodinspect() constとmutator-methodmutate()があるとします:

void f(MyFredList& a) // The MyFredList is non-const{ // Okay to call methods that inspect (look but not mutate/change) the Fred at a: Fred x = a; // Doesn't change to the Fred at a: merely makes a copy of that Fred a.inspect(); // Doesn't change to the Fred at a: inspect() const is an inspector-method // Okay to call methods that DO change the Fred at a: Fred y; a = y; // Changes the Fred at a a.mutate(); // Changes the Fred at a: mutate() is a mutator-method}

ただし、呼び出し元にconst MyFredList aまたはconst MyFredList& aがある場合、aconstsubscriptoperatorを呼び出し、呼び出し元はFredへのconst参照になります。 これにより、発信者はaFredを検査することができますが、発信者がaFredを誤って変更/変更することを防ぎます。

void f(const MyFredList& a) // The MyFredList is const{ // Okay to call methods that DON'T change the Fred at a: Fred x = a; a.inspect(); // Compile-time error (fortunately!) if you try to mutate/change the Fred at a: Fred y; a = y; // Fortunately(!) the compiler catches this error at compile-time a.mutate(); // Fortunately(!) the compiler catches this error at compile-time}

添字演算子およびfuncall演算子のConstオーバーロードは、here、here、here、here、およびhereです。

もちろん、添字演算子以外のものにはconst-overloadingを使用することもできます。

論理状態と物理状態を区別すると、より良いクラスを設計するのにどのように役立ちますか?

これは、クラスを内側からではなく外側から設計することを奨励するため、クラスとオブジェクトを理解して使用しやすく、直感的で、エラーが少なく、 (さて、それはわずかな過度の単純化です。 すべてのifとbutを理解するには、thisanswerの残りの部分を読むだけです!)

これを内側から理解しましょう-あなたは外側からクラスを設計する必要がありますが、この概念に慣れていない場合は、内側から理解する方が簡

内部では、あなたのオブジェクトは物理的な(または具体的な、またはビット単位の)状態を持っています。 クラスが単なるCスタイルのstructであれば、そこにある状態です。外部では、オブジェクトにクラスのユーザーがあり、これらのユーザーはpublicmemberfunctionsとfriendのみを使用するように制限されています。 たとえば、オブジェクトがクラスRectangleでメソッドwidth()height()、およびarea()の場合、ユーザーはそれらの3つがオブジェクトの論理的(または抽象的または意味的)状態のすべ 外部ユーザーにとって、Rectangleobjectは、その領域がその場で計算されている場合でも(たとえば、area()メソッドがobjectの幅と高さの積を返す場合)、実際には領域を持ちます。 実際には、これは重要なポイントです、あなたのユーザーはこれらのメソッドのいずれかをどのように実装するかを知らず、気にしません; あなたのユーザーは、あなたのオブジェクトが論理的に幅、高さ、および面積のameaningwise状態を持っていることを、彼らの観点から認識しています。

area()の例は、論理状態に物理状態で直接実現されていない要素を含めることができる場合を示しています。 クラスは、オブジェクトの物理的な(具体的な、ビット単位の)状態の一部をユーザーから意図的に隠すことがあります—ユーザーがこの隠された状態を読み書きしたり、知ることを可能にするpublicメンバー関数やfriendを意図的に提供しません。 つまり、オブジェクトの物理状態には、オブジェクトの論理状態に対応する要素がないビットがあることを意味します。

この後者のケースの例として、コレクションオブジェクトは、次のルックアップのパフォーマンスを向上させるために、最後のルックアップをキャッ このキャッシュは確かにオブジェクトの物理的な状態の一部ですが、そこにはおそらくユーザーに公開されないinternalimplementationの詳細があります。 あなたが外から考えるならば、簡単なことは何かを伝える-で: コレクションオブジェクトのユーザーがキャッシュ自体の状態を確認するためにnowayを持っている場合、キャッシュは透過的であり、オブジェクトのlogicalstate

私のパブリックメンバー関数の定数は、メソッドがオブジェクトの論理状態または物理状態に対して何をするかに基づいている必要がありますか?

この次の部分を簡単にする方法はありません。 それは傷つくだろう。 最もよい推薦は坐ることである。 そして、あなたの安全のために、近くに鋭い道具がないことを確認してください。

コレクションオブジェクトの例に戻りましょう。 覚えておいてください: 将来のルックアップを高速化するために、最後のルックアップをキャッシュするルックアップ方法があります。

おそらく明白なことを述べてみましょう:lookupメソッドがcollection-objectの論理状態のいずれにも変更を加えないと仮定します。

だから…あなたを傷つける時が来た。 準備はいいか?

ここに来る:lookupメソッドがcollection-objectの論理状態を変更しないが、collection-objectの物理状態を変更する場合(実際のキャッシュに非常に実際の変更を行います)、lookupメソッ

答えは”はい”です。 (すべてのルールには例外があるので、「はい」には実際にアスタリスクが付いているはずですが、大部分の場合、答えは「はい」です。)

これは、”物理的なconstよりも”論理的なconst“についてのすべてです。”それは、constでamethodを飾るかどうかについての決定は、その方法が論理状態を変更しないかどうかに主に左右されるべきであることを意味します(あなたは座っていますか? メソッドがオブジェクトの非常に現実的な物理状態に非常に現実的な変化を起こすかどうかにかかわらず、(座っていることをお勧めします)。

沈んでいなかった場合、またはまだ痛みがない場合は、二つのケースに分けていじくりましょう:

  • メソッドがオブジェクトの論理状態の一部を変更した場合、それは論理的にミューテータです。constevenifであってはなりません(実際に起こるように!)このメソッドは、オブジェクトの具体的な状態の物理的なビットを変更しません。
  • 逆に、メソッドは論理的にはインスペクタであり、オブジェクトの論理状態の一部を決して変更しない場合は、(実際に起こったとしても)constでなければなりません。)このメソッドは、オブジェクトの具体的な状態の物理ビットを変更します。

混乱している場合は、もう一度読んでください。

あなたが混乱していないが怒っているなら、良い:あなたはまだそれを好きではないかもしれませんが、少なくともあなたはそれを理解しています。 深呼吸をして、私の後に繰り返してください:”メソッドのconstネスは、オブジェクトの外側から理にかなっているはずです。”メソッドの定数はオブジェクトのユーザーにとって意味をなさなければならず、それらのユーザーはオブジェクトの論理状態のみを見ることができます。”

まだ怒っているなら、申し訳ありませんが、それはそれです。 それを吸うし、それと一緒に暮らす。 はい、例外があります; すべてのルールはそれらを持っています。 しかし、原則として、主に、この論理的なconst概念はあなたにとって良いものであり、あなたのソフトウェアにとって良いものです。

もう一つ。 これは無意味になりますが、メソッドがオブジェクトのlogicalstateを変更するかどうかについて正確に説明しましょう。 あなたがクラスの外にいる場合—あなたは通常のユーザーですが、実行できるすべての実験(呼び出すメソッドのすべてのメソッドorsequence)は、そのlookupメソッドを Lookup関数がfutureメソッドの将来の動作を変更した場合(単に高速化するだけでなく、結果を変更し、戻り値を変更し、theexceptionを変更した場合)、lookupメソッドはオブジェク しかし、lookupメソッドが、おそらくいくつかのことをより速くする以外に何も変更しなかった場合、それはインスペクタです。

constメンバー関数でデータメンバーに”見えない”変更を加えたい場合はどうすればよいですか?

mutableを使用します(または、最後の手段としてconst_castを使用します)。

わずかな割合の検査官が、externalusersでは観察できないオブジェクトの物理状態に変更を加える必要があります。

たとえば、前に説明したコレクションオブジェクトは、次の検索のパフォーマンスを向上させるために、最後の検索をキャッシュしました。 この例では、キャッシュはcollection-objectのpublic interface(タイミング以外)のどの部分でも直接監視できないため、その存在と状態はobjectのlogical状態の一部ではないため、外部ユー Lookupメソッドは、少なくとも現在の実装では、オブジェクトの物理状態を変更するという事実に関係なく、オブジェクトの論理状態を決して変更しな

メソッドが物理的ではあるが論理的ではない状態を変更する場合、メソッドは通常、インスペクタメソッドであるため、constとマークする必要があります。 コンパイラがconstメソッドがthisオブジェクトの物理状態を変更しているのを見ると、文句を言います—コードにエラーメッセージが表示されます。

C++コンパイラ言語は、この論理的なconst概念を受け入れるのに役立つmutableキーワードを使用します。 この場合、mutableキーワードでキャッシュをマークすると、コンパイラはconstメソッド内または他のconstポインタまたは参照を介して変更することが許可されてい 私たちの専門用語では、mutableキーワードは、論理状態の一部ではないオブジェクトの物理状態の部分をマークします。

mutableキーワードは、データメンバーの宣言の直前、つまりconstを置くことができる場所と同じ場所にあります。 もう1つのアプローチは、おそらくconst_castキーワードを介して、thisポインタのconst‘nessをキャストすることではありません:

Set* self = const_cast<Set*>(this); // See the NOTE below before doing this!

この行の後、selfthisと同じビット、つまりself == thisを持ちますが、selfconst Set*ではなくSet*です(技術的にはthisconst Set* constですが、右端のconstはこの議論とは無関係です)。つまり、selfを使用してthisが指すオブジェクトを変更することができます。

注:const_castで発生する可能性のある非常に低いエラーがあります。 それは三つの非常にまれなものが同時に組み合わされたときにのみ起こります: mutableであるべきデータメンバー(上記のような)、mutableキーワードをサポートしていないcompilerthat、および/またはそれを使用しないプログラマ、およびconstであるとoriginallydefinedされたオブジェクこの組み合わせは非常にまれであり、決して起こらないかもしれませんが、それが起こった場合、コードが機能しない可能性があります(標準では動作

const_castを使用したい場合は、代わりにmutableを使用してください。 つまり、anobjectのメンバーを変更する必要があり、そのオブジェクトがconstへのポインタで指されている場合、最も安全で簡単なことはメンバーの宣言にmutableを追 実際のオブジェクトがconstではないことが確実な場合はconst_castを使用できます(たとえば、オブジェクトがSets;のように宣言されていることが確実な場合)が、オブジ

マシンZのコンパイラYのバージョンXがconstオブジェクトの非mutableメンバーを変更できると書いてはいけません。 私は気にしません—それは言語に応じて違法であり、あなたのコードはおそらく異なるコンパイラや同じコンパイラの別のバージョン(アップグレード)で ノーと言ってくれ 代わりにmutableを使用してください。 コードを書くそれは動作することが保証されており、壊れていないように見えるコードではありません。

const_castは最適化の機会が失われたことを意味しますか?

理論的には、はい、実際には、いいえ。

言語がconst_castを非合法化したとしても、constmemberfunction呼び出し全体でレジスタキャッシュをフラッシュしないようにする唯一の方法は、エイリアシングの問題を解、オブジェクトを指す非constポインタがないことを証明するため)。 これはまれなケースでのみ発生する可能性があります(オブジェクトがconstmemberfunction呼び出しのスコープで構築され、オブジェクトの構築とconstメンバ関数呼び出しの間のすべての非constメンバ関数呼び出しが静的にバインドされ、これらの呼び出しのすべてがinlinedであり、コンストラクタ自体がinlinedであり、メンバ関数がコンストラクタ呼び出しがinlineである場合)。

なぜコンパイラはconst int*でそれを指した後にintを変更することを許可しますか?

const int* p“は”p*pを変更しないことを約束することを意味するので、”not”*pは変更しないことを約束します。”

const int*intを指すようにすると、constintではありません。 intconst int*を介して変更することはできませんが、他の誰かが同じint(”エイリアス”)を指すint*(注:constなし)を持っている場合、そのint*を使用してintを変更できます。 例えば:

void f(const int* p1, int* p2){ int i = *p1; // Get the (original) value of *p1 *p2 = 7; // If p1 == p2, this will also change *p1 int j = *p1; // Get the (possibly new) value of *p1 if (i != j) { std::cout << "*p1 changed, but it didn't change via pointer p1!\n"; assert(p1 == p2); // This is the only way *p1 could be different }}int main(){ int x = 5; f(&x, &x); // This is perfectly legal (and even moral!) // ...}

main()f(const int*,int*)は、週の異なる日にコンパイルされる異なるコンパイル単位にある可能性があることに注意してください。 その場合、コンパイラがコンパイル時にエイリアシングを検出できる方法はありません。 したがって、私たちはこの種のことを禁止する言語ルールを作ることができる方法はありません。 実際には、一般的には、同じものを指す多くのポインタを持つことができる機能と考えられているので、そのようなaruleを作りたくないでしょう。 これらのポインタの1つが、基礎となる「もの」を変更しないことを約束しているという事実は、ポインタによって行われた約束に過ぎません; それは”事”によって作られた約束ではありません。

“const Fred*p”は*pが変更できないことを意味しますか?

いや! (これはintポインタのエイリアシングに関するFAQに関連しています。)

const Fred* p” つまり、Fredはポインタpを介して変更することはできませんが、constを経由せずにオブジェクトを取得する他の方法があるかもしれません(Fred*などのエイ たとえば、同じFredオブジェクト(エイリアシング)を指す2つのポインタ”const Fred* p“と”Fred* q“がある場合、pointerqを使用してFredオブジェクトを変更できますが、pointerpは変更できません。Foo**→const Foo**の変換中にエラーが発生するのはなぜですか?

Foo**const Foo**の変換は無効で危険です。

C++は(安全な)変換を許可しますFoo*Foo const*ですが、暗黙的に変換しようとするとエラーが発生しますFoo**const Foo**

そのエラーがなぜ良いのかの根拠を以下に示します。 しかし、最初に、ここで最も一般的な解決策です:単純にconst Foo**const Foo* const*に変更します:

class Foo { /* ... */ };void f(const Foo** p);void g(const Foo* const* p);int main(){ Foo** p = /*...*/; // ... f(p); // ERROR: it's illegal and immoral to convert Foo** to const Foo** g(p); // Okay: it's legal and moral to convert Foo** to const Foo* const* // ...}

Foo**const Foo**からの変換が危険である理由は、キャストなしでconst Fooオブジェクトを静かに誤って変更できるからです:

class Foo {public: void modify(); // make some modification to the this object};int main(){ const Foo x; Foo* p; const Foo** q = &p; // q now points to p; this is (fortunately!) an error *q = &x; // p now points to x p->modify(); // Ouch: modifies a const Foo!! // ...}

もしq = &p行が合法であれば、qpを指しているでしょう。 次の行*q = &xp自体を*qpであるためxを指すように変更します。 それはconst修飾子を失ってしまったので、悪いことです:pFoo*ですが、xconst Fooです。 p->modify()行はpのreferentを変更する機能を利用しますが、これは実際の問題です。const Fooを変更することになったためです。

類推すると、あなたが合法的な変装の下で犯罪者を隠す場合、彼はその変装に与えられた信頼を悪用することができます。それは悪いです。

ありがたいことに、C++ではこれを行うことができません。q = &p行はc++コンパイラによってcompile-timeerrorとしてフラグが付けられています。 注意:コンパイル時のエラーメッセージの周りにポインタをキャストしないでください。 ちょうどノーと言う!

(注:これとDerived**からBase**への変換の禁止との間には概念的な類似性があります。)

コメントを残す

メールアドレスが公開されることはありません。