3

c++ では、右辺値または一時オブジェクトのみが const 参照にバインドできることを理解しています。(またはそれに近いもの...)

たとえば、関数があり、次のようdoStuff(SomeValue & input)
SomeValue getNiceValue()定義されているとします。

/* These do not work */
app->doStuff(SomeValue("value1"));
app->doStuff(getNiceValue());

/* These all work, but seem awkward enough that they must be wrong. :) */

app->doStuff(*(new SomeValue("value2")));

SomeValue tmp = SomeValue("value3");
app->doStuff(tmp);

SomeValue tmp2 = getNiceValue();
app->doStuff(tmp2);

だから、3つの質問:

  1. doStuff()orの署名を自由に変更できないためgetNiceValue()、渡したいものには常に何らかの「名前」を使用する必要があるということdoStuffですか?

  2. 仮説として、関数のシグネチャを変更できた場合、この種の共通パターンはありますか?

  3. 新しい C++11 標準は、物事をまったく変更しますか? C++11 でより良い方法はありますか?

ありがとうございました

4

5 に答える 5

6

この場合の明らかな問題は、doStuffそのパラメーターが非 const 参照として宣言されている理由です。参照されたオブジェクトを実際に変更しようとする場合、関数シグネチャを const 参照に変更することはオプションではありません (少なくともそれ自体ではできません)。

とにかく、「rvalue-ness」は、一時オブジェクト自体のプロパティではなく、一時オブジェクトを生成した式のプロパティです。一時オブジェクト自体は簡単に左辺値にすることができますが、それを生成した式が右辺値式であるため、右辺値として表示されます。

「右辺値から左辺値へのコンバーター」メソッドをクラスに導入することで、これを回避できます。たとえば、

class SomeValue {
public:
  SomeValue &get_lvalue() { return *this; }
  ...
};

非 const 参照を一時変数にバインドできるようになりました。

app->doStuff(SomeValue("value1").get_lvalue());
app->doStuff(getNiceValue().get_lvalue());

確かに、これはあまりエレガントに見えませんが、不注意でそのようなことをするのを防いでくれるので、良いことだと思われるかもしれません。もちろん、temporary の有効期間は完全な式の最後まで延長され、それ以上延長されないことを覚えておくのはユーザーの責任です。

または、クラスは単項&演算子を (自然なセマンティクスで)オーバーロードできます。

class SomeValue {
public:
  SomeValue *operator &() { return this; }
  ...
};

その後、同じ目的に使用できます

app->doStuff(*&SomeValue("value1"));
app->doStuff(*&getNiceValue());

&ただし、この回避策のためだけに演算子をオーバーライドすることはお勧めできません。また、一時オブジェクトへのポインターを作成することもできます。

于 2012-08-14T00:06:07.960 に答える
4

std::forward通常、値カテゴリを「変換」する方法です。constただし、非への参照が右辺値にバインドされないのと同じ理由で、左辺値として転送するときに右辺値を受け入れることは禁止されています。そうは言っても、オーバーロードしたくないと仮定するとdoStuff(そうでない場合は、Hinnantの回答を参照)、ユーティリティを自分で作成できます。

template<typename T>
T& unsafe_lvalue(T&& ref)
{ return ref; }

そして、そのようにそれを使用してください:app->doStuff(unsafe_lvalue(getNiceValue()))。煩わしい変更は必要ありません。

于 2012-08-14T02:25:31.893 に答える
4
  1. doStuff()orの署名を自由に変更できないためgetNiceValue()、渡したいものには常に何らかの「名前」を使用する必要があるということdoStuffですか?

はい。inputこのシグネチャは、「out」パラメータとして使用することを前提としています。したがって、 の作成者はdoStuff、クライアントが無名オブジェクトを渡す場合、それはコンパイル時に最も適切に検出される論理エラーであると考えています。

  1. 仮説として、関数のシグネチャを変更できた場合、この種の共通パターンはありますか?

C++11 でのみ、次のように変更またはオーバーロードできます。

doStuff(SomeValue&& input);

input右辺値にのみバインドするようになりました。オーバーロードした場合、元のオーバーロードは左辺値を取得し、新しいオーバーロードは右辺値を取得します。

  1. 新しい C++11 標準は、物事をまったく変更しますか? C++11 でより良い方法はありますか?

はい、上記の右辺値参照のオーバーロードを参照してください。

于 2012-08-14T00:23:57.053 に答える
1

doStuff に渡す値には常に名前を使用する必要があります。この理由については、「非定数参照が一時オブジェクトにバインドできないのはなぜですか?」で詳しく説明されています。. 簡単にまとめると、参照を渡すことは、doStuff が参照する値を変更できることを意味し、参照の値を一時変数に変更することは、コンパイラーが許可してはならないことです。

最初の解決策は、決して解放されないヒープにメモリを割り当てるため、避けたいと思います。

これを解決するための一般的なパターンは、doStuff の署名を const 参照を取るように変更することです。

于 2012-08-14T00:08:41.847 に答える
0

残念ながら、答えは、 に渡す名前付きオブジェクトが必要だということだと思いますdoStuff。この分野で柔軟性を提供する C++11 の機能はないと思いますし、このような状況での設計パターンも聞いたことがありません。

これがプログラムで頻繁に発生すると予想される場合は、現在のアプリのニーズにより適したインターフェイスを記述します。1回限りの場合は、一時オブジェクトを作成して結果を保存する傾向があります(あなたが行ったように)。

于 2012-08-14T00:01:29.587 に答える