Constではない信じられないほどのConst参照
NamedTypeライブラリで作業している間、私は困惑して唖然とした状況に遭遇しました:それが参照するオブジェクトの変更を可能にするconst参照。 const_cast
なし。 mutable
なし。 袖の上の何もなし。
これはどうすればいいですか? そして、そのconst参照でconst
を強制する方法は?
constではないconst参照
ここでは、この奇妙な参照が発生する状況の説明です。 次のラッパークラスを考えてみましょう:
template<typename T>class Wrapper{public: Wrapper(T const& value) : value_(value) {} T const& get() const { return value_; } private: T value_;};
このWrapper
クラスはT
型の値を格納し、get()
メソッドを介してアクセスできます(それとは独立して、”get”が良い名前であるかどうか疑問に思っている場合は、こ 重要な部分は、get()
メソッドはconst参照を返すため、格納された値への読み取り専用アクセスのみを提供することです。
それを確認するには、例えばint
でテンプレートをインスタンス化しましょう。 get()
メソッドを使用して格納された値を変更しようとすると、コンパイルに失敗します(下の実行ボタンをクリックしてみてください)。
私たちはget()
を読み取り専用にしたいので、これは問題ありません。
しかし、今度はWrapper
をint&
でインスタンス化しましょう。 このためのユースケースの一つは、コピーを避けるため、または例えばa
の潜在的な変更を追跡することです。 次に、get()
によって返されたconst参照を使用して格納された値を変更してみましょう:
そして、あなたが実行ボタンを押すと、あなたはそれが表示されます…それはコンパイルされます! そして、特別なことは何も起こらなかったように、const参照を介して変更された値を出力します。
これは不可解ではないですか? const
が提供する安心感を感じませんか? 私たちは誰を信頼できますか?
この参照で何が起こっているのかを理解しようとしましょう。
Const参照またはconstへの参照ですか?問題の核心は、参照がconst参照であるが、constへの参照ではないということです。
それが何を意味するのかを下にするために、テンプレートのインスタンス化で何が起こっているのかを段階的に分解してみましょう。
get()
メソッドはconst T&
を返し、T
はtemplate T
から来ます。 2番目のケースでは、T
はint&
なので、const T&
はconst (int&) &
です。
このconst (int&)
を詳しく見てみましょう。 int&
はint
を参照する参照であり、そのint
を変更することが許可されています。 しかし、const (int&)
は参照int&
つまりconst
であり、参照自体を変更することはできません。
最初に参照を変更するとはどういう意味ですか? 理論的には、それは別のオブジェクトを参照するか、何も参照しないことを意味します。 しかし、これらの可能性は両方ともC++では違法です。 それはそれの人生の全過程の間に同じオブジェクトを指します。
だからconst
であることは、再バインドすることができないので、常にconst
であるため、参照のためにあまり言いません。 これは、const (int&)
が事実上int&
と同じ型であることを意味します。
はget()
を(int&) &
に戻します。 そして、参照の崩壊のルールによって、これはint&
です。
したがって、get
はint&
を返します。 そのインターフェースはT const&
と書かれていますが、実際はint&
です。 本当に表現力のあるコードではありませんか?
const参照をconst
への参照にする方法今、問題を理解する最初のステップを過ぎているので、どうすれば修正できますか? つまり、get()
を変更できない格納された値への参照を返すにはどうすればよいですか?
これを行う1つの方法は、参照の中にconst
を明示的に挿入することです。 これを行うには、参照を削除し、const
を追加し、参照を元に戻すことができます。 参照を削除するには、C++11ではstd::remove_reference
を使用し、Cではより便利なstd::remove_reference_t
を使用します++14:
実行をクリックするとわかるように、Wrapper
に格納されている値を変更しようとすると、予想されるコンパイルエラーが表示されるようになりました。
これは安全なように見えるコードですが、実際にはそうではなく、これが最も危険な種類であるため、この参照で何が起こっているのかを理解する
ページに埋め込まれた上記の遊び場のコードで遊ぶことができます。 そして、この問題の背後にある理由を理解するのを手伝ってくれたstack overflowユーザー rakete1111に大きな感謝します。
あなたも好きかもしれません
- 最も厄介な解析:それを見つけてすぐに修正する方法