C ++で値によって参照を渡す方法は?
-
19-08-2019 - |
質問
C ++でam不変型(クラス)を作成しようとしています
すべてのメソッド<!> quot; aka member functions <!> quot;オブジェクトを変更せず、代わりに新しいインスタンスを返します。
多くの問題に直面していますが、それらはすべてC ++の参照型を中心に展開しています。
1つの例は、同じクラスタイプのパラメーターを参照で渡す場合です:
Imm Imm::someOp( const Imm& p_im ) const
{
...
p_im = p_im.someOtherOp(); //error, p_im is const, can't modify it!
...
}
エラーは、値を参照渡しすることで発生します。 代わりに、値で参照を渡す場合、上記のエラー行はエラーになりません!
Java / C#の例を検討する
class Imm
{
...
Imm someOp( Imm p_im )
{
....
p_im = p_im.someOtherOp(); //ok, you're not modifying the
//original object, just changing the local reference
....
}
....
}
C ++でこのようなことを行うにはどうすればよいですか?ポインタを使用できることはわかっていますが、メモリ管理の混乱に陥ります。私は誰がオブジェクトへの参照を所有しているか心配したくありません。
理想的には、Pythonで不変の文字列のようにクラスを設計したいと思います。それらは不変であり、不変であることさえ知らずに使用でき、期待どおりに動作します。動作します。
編集
もちろん、値渡しするか、一時変数を使用することで回避できます(現在、これを行っています)。私が尋ねているのは<!> quot; C ++で値によって参照を渡す方法<!> quot;
答えはSTLの何かを中心に展開することを期待しています。現在、smart_ptrファミリーのテンプレートを検討しています。
更新
回答のおかげで、ポインターからのエスケープはないことに気付きました。 (私の他の質問を参照してください。これは実際にフォローアップです)
解決
JavaおよびC#では、実際に参照を処理しているわけではありません。これらは、ハンドルまたはポインターのようなものです。 C ++の参照は、実際には元のオブジェクトの別の名前であり、オブジェクトへのポインターではありません(ただし、ポインターを使用して実装される場合があります)。参照に値を割り当てると、オブジェクト自体に割り当てられます。参照を初期化するには=
文字を使用できるという点で混乱がありますが、これは初期化であり、割り当てではありません。
Imm im, im2, im3;
Imm &imr = im; // initialize a reference to im
imr = im2; // assign im2 to imr (changes the value of im as well)
Imm *imp = &im; // initialize a pointer to the address of im
imp = &im3; // assign the address of im3 to imp (im is unnaffected);
(*imp) = im2; // assign im2 to imp (modifies im3 as well).
<!> quot;値による参照<!> quotを特に渡したい場合;あなたは本質的に用語の矛盾を求めています。定義により、参照は参照渡しされます。他の場所で指摘したように、ポインタを値で渡すことも、そのままの値で渡すこともできます。本当に必要な場合は、クラス内の参照を保持し、値で渡します:
struct ImmRef
{
Imm &Ref;
ImmRef(Imm &ref) : Ref(ref) {}
};
また、参照に適用されるconstは、参照ではなく、参照されるオブジェクトを定数にすることに注意してください。参照は常にconstです。
他のヒント
定義上、割り当ては定数操作ではありませんか?
const参照に何かを割り当てようとしているように見えますが、これはconst参照の概念を完全に無効にします。
参照ではなくポインタを探しているのではないかと思います。
C ++ではそのようには機能しません。
オブジェクトへの参照を渡すとき、実際にはオブジェクトのメモリ内のアドレスを渡します。参照を他のオブジェクトに再配置することもできないため、C ++の格言<!> quot;参照はオブジェクトです。<!> quot;変更するにはコピーを作成する必要があります。 Javaがバックグラウンドでこれを行います。 C ++、コピーするだけです。
呼び出すメソッドをconstに設定するのを忘れていませんか?
編集:そのため、constが修正されました。
たぶんあなたは次のようなことをすべきでしょう
Imm & tmp = p_im.someOtherOp();
次に、tmp変数をさらに操作します。
const <!> ampとして変数またはパラメーターを設定した場合。割り当てることはできません。
これをチェックして、一時的な存続期間を確認してください http://herbsutter.wordpress.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
Imm Imm::someOp( const Imm& p_im ) const
{
...
//Imm& im = p_im.someOtherOp(); // will *not* work
const Imm& im = p_im.someOtherOp(); // will work, but you get a const reference
...
}
ただし、boost :: shared_ptrを使用できます
shared_ptr<Imm> Imm::someOtherOp() const
{
shared_ptr<Imm> ret = new Imm;
...
return ret;
}
shared_ptr<Imm> Imm::someOp(const share_ptr<Imm>& p_im) const
{
shared_ptr<Imm> im = p_im->someOtherOp();
}
受信した引数の新しいコピーを作成する必要があります。いくつかの同等の方法で必要なことを行うことができます:1)値で渡すことができます:
Imm Imm::someOp( Imm im ) const {
im = im.someOtherOp(); // local im is a copy, original im not modified
return im; // return by value (another copy)
}
または2)参照渡しして明示的にコピーを作成できます:
Imm Imm::someOp( const Imm & im ) const {
Imm tmp = im.someOtherOp(); // local tmp is a copy
return tmp; // return by value (another copy)
}
両方のフォームは同等です。
C ++には、不変の型よりも優れたものがあります<!>#8212; const
。単一のタイプは、ニーズに応じて可変または非可変にすることができます。とはいえ、短命(近)コピーを処理するための有用なパターンがあります:
void f(const X &x) {
// Trivial case: unconditional copy
X x2=transform(x);
// Less trivial: conditional copy
std::optional<X> maybe;
const X &use=need_copy ? maybe.emplace(transform(x)) : x;
use.go(); // whichever, x or *maybe
} // *maybe destroyed iff created, then x2 destroyed
std::unique_ptr
はC ++ 17より前の同様の方法で使用できますが、関数はもちろんstd::bad_alloc
をスローできます。