14

私は以下に書かれたコードが違法であることを知っています

void doSomething(std::string *s){}
int main()
{
     doSomething(&std::string("Hello World"));
     return 0;
}

その理由は、一時オブジェクトのアドレスを取得することが許可されていないためです。しかし、私の質問はなぜですか?

次のコードを考えてみましょう

class empty{};
int main()
{
      empty x = empty(); //most compilers would elide the temporary
      return 0;
}

ここで受け入れられた答えは言及しています

「通常、コンパイラは、メモリのまったく同じ場所にある 2 つのオブジェクトとして構築された一時オブジェクトとコピーを考慮し、コピーを回避します。」

ステートメントによると、一時的なものはいくつかのメモリ位置に存在していたと結論付けることができ(したがって、そのアドレスが取得された可能性があります)、コンパイラは、一時的なものと同じ場所にインプレースオブジェクトを作成することにより、一時的なものを削除することにしました。 .

これは、テンポラリーのアドレスを取得できないという事実と矛盾しますか?

また、戻り値の最適化がどのように実装されているかも知りたいです。誰かが RVO の実装に関連するリンクまたは記事を提供できますか?

4

7 に答える 7

15
&std::string("Hello World")

これに伴う問題はstd::string("Hello World")、一時オブジェクトを生成することではありません。問題は、式std::string("Hello World")が一時オブジェクトを参照する右辺値式であるということです。

すべての右辺値にアドレスがあるわけではないため(すべての右辺値がオブジェクトであるとは限らないため)、右辺値のアドレスを取得することはできません。次のことを考慮してください。

42

これは整数リテラルであり、一次式と右辺値です。これはオブジェクトではなく、(おそらく)アドレスを持っていません。 &42無意味です。

はい、最初の例のように、右辺値はオブジェクトを参照する場合があります。問題は、すべての右辺値がオブジェクトを参照しているわけではないことです。

于 2010-11-29T05:56:43.343 に答える
7

長い答え:

[...]一時的なものは何らかの記憶場所に存在していたと結論付けることができます

定義により:

  • 「一時」の略: 一時オブジェクト
  • オブジェクトはストレージの領域を占有します
  • すべてのオブジェクトにはアドレスがあります

したがって、テンポラリーにアドレスがあることを示すのに、非常に精巧な証明は必要ありません。これは定義によるものです。

OTOH、アドレスをフェッチするだけでなく、組み込みの address-of 演算子を使用しています。組み込みの address-of 演算子の仕様では、左辺値が必要であることが示されています。

  • &std::string()std::string()右辺値であるため、形式が正しくありません。実行時に、この式のこの評価は副作用として一時オブジェクトを作成し、式は作成されたオブジェクトを参照する右辺値を生成します。
  • &(std::string() = "Hello World")std::string() = "Hello World"lvalueであるため、整形式です。定義により、左辺値はオブジェクトを参照します。この左辺値が参照するオブジェクトは、まったく同じ一時オブジェクトです

簡潔な答え:

これがルールです。一部の人々がでっちあげている (正しくない、不健全な) 正当化は必要ありません。

于 2011-12-05T09:11:07.080 に答える
4

$5.3.1/2 - 「単項 & 演算子の結果は、そのオペランドへのポインターです。オペランドは左辺値または修飾 ID でなければなりません。

などの表現

99

A() // where A is a user defined class with an accessible 
    // and unambiguous default constructor

はすべて Rvalue です。

$3.10/2 - 「左辺値はオブジェクトまたは関数を参照します。一部の右辺値式 (クラスまたは cv 修飾クラス型のもの) もオブジェクトを参照します。47)」

そして、これは私の推測です: Rvalues はストレージを占有する可能性がありますが (オブジェクトの場合など)、C++ 標準では、組み込み型との統一性を維持するためにアドレスを取得することは許可されていません。

ただし、ここに興味深いことがあります。

void f(const double &dbl){
   cout << &dbl;
}

int main(){
   f(42);
}

式「42」は、「const double への参照」にバインドされている Rvalue であるため、double 型の一時オブジェクトを作成します。このテンポラリのアドレスは、関数 'f' 内で取得できます。ただし、「f」内では、これは実際には一時的または Rvalue ではないことに注意してください。'dbl' などの名前を付けた時点で、'f' 内の左辺値式として扱われます。

これはNRVOに関するものです(類似)

于 2010-11-29T06:10:03.970 に答える
3

テンポラリーは、C++ の「右辺値」の一例です。その型内の値を純粋に表すことになっています。たとえば、42プログラム内の 2 つの異なる場所に記述した場合、 のインスタンスは、42おそらく異なる時間に異なる場所にあるにもかかわらず、区別できません。アドレスを取得できない理由は、アドレスが存在する必要があることを指定するために何かを行う必要があるためです。そうしないと、アドレスの概念が意味的に汚れており、直感的でないためです。

「何かをする」という言語要件はいくぶん恣意的ですが、C++ プログラムをよりクリーンにします。人々が一時的な住所を取る習慣を身につけたら、それはひどいことです. アドレスの概念は、ライフタイムの概念と密接に結びついているため、「瞬間的な」値にアドレスがないことは理にかなっています。それでも、注意すれば、アドレスを取得して、標準で許可されている有効期間内に使用できます。

ここの他の回答にはいくつかの誤りがあります:

  • 「すべての右辺値にアドレスがあるわけではないため、右辺値のアドレスを取得することはできません。」— すべての左辺値にアドレスがあるわけではありません。単純なループに参加し、その後使用されないタイプの典型的なローカル変数にはint、レジスタが割り当てられる可能性がありますが、スタックの場所は割り当てられません。メモリの場所がないということは、アドレスがないことを意味します。ただし、アドレスを取得すると、コンパイラはそれにメモリ位置を割り当てます。同じことが、参照にバインドされる可能性のある右辺値にも当てはまりconstます。「のアドレス42」は次のように取得できます。

    int const *fortytwo_p = & static_cast<int const &>( 42 );
    

    もちろん、;一時的なものは一時的なものであるため、その後のアドレスは無効です。これにより、マシンが無意味に 42 をスタックに格納する可能性があるため、余分な命令が生成される可能性があります。

    C++0x では、prvalue をストレージとは関係なく式の値として定義し、glvalue をその内容とは関係なくストレージの場所として定義することで、概念をクリーンアップしていることに言及する価値があります。これはおそらく、そもそも C++03 標準の意図でした。

  • 「その後、一時的なものを変更できますが、これは無意味です。」— 実際、副作用のある一時的なものは変更すると便利です。このことを考慮:

    if ( istringstream( "42" ) >> my_int )
    

    これは、数値を変換し、変換が成功したことを確認するための優れたイディオムです。これには、一時的なものを作成し、その上で変更関数を呼び出してから破棄することが含まれます。無意味とはほど遠い。

于 2010-11-29T06:34:45.203 に答える
2

取ることができますが、一時的なものがなくなると、ダングリングポインタが残ります。

編集

反対派の場合:

const std::string &s = std::string("h");
&s;

合法です。s一時的なものへの参照です。したがって、一時オブジェクトのアドレスを取得できます。

EDIT2

バインドされた参照、バインドされているもののエイリアスです。したがって、一時への参照は、その一時の別名です。したがって、上記の段落の2番目のステートメントが成り立ちます。

OPの質問は(彼が使用する単語の観点から)一時的なものについてであり、彼の例は右辺値についてです。これらは2つの異なる概念です。

于 2010-11-29T05:57:01.243 に答える
0

1 つの理由は、あなたの例ではメソッドに一時的な書き込みアクセス権を与えることです。これは無意味です。

あなたが提供した引用は、この状況に関するものではなく、初期化子を持つ宣言子で許可されている特定の最適化です。

于 2010-11-29T06:15:47.880 に答える