web-dev-qa-db-ja.com

文のコピー値を返します

スコープの問題のため、これについて疑問に思っています。たとえば、コードを考えます

typedef struct {
    int x1;/*top*/
    int x2;/*bottom*/
    int id;
} subline_t;



subline_t subline(int x1, int x2, int id) {
    subline_t t = { x1, x2, id };
    return t;
}

int main(){
    subline_t line = subline(0,0,0); //is line garbage or isn't it? the reference
    //to subline_t t goes out of scope, so the only way this wouldn't be garbage
    //is if return copies
}

だから私の質問は、returnステートメントは常にコピーされますか?この場合、それはうまくいくように思えるので、私はリターンがコピーを行うと信じさせられます。コピーする場合、すべての場合にコピーしますか?

64
ldog

はい、その場合はコピーが作成されます。このように関数宣言を変更した場合:

subline_t &subline(int x1, int x2, int id) {

その後、コピーは作成されません。ただし、特定のケースでは、スタックに割り当てられたオブジェクトへの参照を返すことは無効です。問題は、呼び出し元がオブジェクトを使用する前にオブジェクトが破棄されて無効になることです。

これは、C++の一般的な 戻り値の最適化 に関連しており、説明した場合に実際のコピー操作を行うことを回避できます。最終結果は、コピーが行われた場合と同じです(またはそうあるべきです)が、最適化に注意する必要があります。この最適化の存在は、場合によっては、プログラムの観察可能な動作を変更する可能性があります。

42
Greg Hewgill

あなたの場合、それはコピーを返します

あなたのコードが

subline_t& subline(int, int)

その後、参照が返され、未定義の動作が発生します。

4
Tom

はい、structを返すように宣言された関数の場合、そのような構造体のreturnはそれをコピーします(ただし、コンパイラーは、最適化を証明できる場合に限り、コピーを最適化する権限を与えられます)意味的に無害である場合、「あたかも」コピーが保証されているかのように推論できます)。

ただし、didこれをCではなくC++としてタグ付けするので、代わりにstructにコンストラクタを指定しないのはなぜですか?より明確で直接的なようです...!-)

3
Alex Martelli

常にコピーを返します。

戻り時にオブジェクトをコピーすることによるパフォーマンスの低下を避けたい場合は、ポインターを宣言し、newを使用してオブジェクトのインスタンスを構築し、ポインターを返すことができます。その場合、ポインターはコピーされますが、オブジェクトはコピーされません。

3
csj

はい、戻りはコピーです

subline_t subline(int x1, int x2, int id) {
        subline_t t = { x1, x2, id };
        return t;
}

リファラーを配置する場合、そのコピーではありません

subline_t & subline(int x1, int x2, int id) {
        subline_t t = { x1, x2, id };
        return t; // will result in corruption because returning a reference
}
2
Andrew Keith

コピーを返します。これは、あなたがやりたいことです。参照を返すように変更すると、行への割り当てで未定義の動作が発生します。

ただし、C++でこれを行う慣用的な方法は、コンストラクターと割り当てリストを使用することです。これにより、コードとデータ構造がよりよくカプセル化され、コンパイラーが自由に構築/破壊/コピーできる中間オブジェクトの過剰を回避できます。

struct subline_t {
        int x1;/*top*/
        int x2;/*bottom*/
        int id;

// constructor which initialises values with assignment list.
  subline_t(int the_x1, int the_x2, int the_id) :
    x1(the_x1),
    x2(the_x2),
    id(the_id)
  {
  }
};


int main(){
    subline_t line2(0,0,0); // never requires a copy or assignment.
}
1
Roddy

C++でのオブジェクトの参照は、参照ではなく値によって行われます。

subline_t tへの参照が範囲外になります

いいえ、オブジェクトはコピーされます。

returnステートメントは常にコピーしますか

はい、そうではありません...意味的にはコピーのように動作しますが、コピーコンストラクタを保存する戻り値の最適化と呼ばれるものがあります。

foo make_foo()
{
    foo f(1,2,3);
    return f;
}

foo ff=make_foo(); /// ff created as if it was created with ff(1,2,3) -- RVO
foo ff2;
ff2=make_foo(); /// instance of foo created and then copied to ff2 and then old
                /// instance destroyed
1
Artyom

返されたクラスまたは構造体は、コンパイラがコピー省略を使用するかどうかに応じて、コピーされる場合とされない場合があります。 コピーの省略と戻り値の最適化とは何ですか? の答えを参照してください。要するに、コピーされるかどうかは多くのことに依存します。

もちろん、参照を返すことでコピーを回避できます。あなたの例の場合、ローカル構造体はスタックに割り当てられているため、参照を返すことは無効です(コンパイラは許可しますが)。したがって、返される参照は割り当て解除されたオブジェクトを参照します。ただし、オブジェクトが関数に(直接またはオブジェクトのメンバーとして)渡された場合、そのオブジェクトへの参照を安全に返すことができ、コピーオンリターンを回避できます。

最後に、コピーの省略を信頼できず、コピーを避けたい場合は、参照の代わりにunique_ptrを使用して返すことができます。オブジェクト自体はコピーされませんが、unique_ptr自体はコピーされる場合とされない場合があります(これもコピーの省略によって異なります!)。ただし、unique_ptrのコピーの省略が何らかの理由で発生しない場合、unique_ptrのコピー/移動は非常に安価です。

unique_ptrを使用した例を次に示します。

#include <memory>

struct A {
public:
  int x;
  int y;

  A(int x, int y) : x(x), y(y) {
  }
};

std::unique_ptr<A> returnsA() {
  return std::make_unique<A>(3, 4);
}

int main() {
  auto a = returnsA();
}

(残念ながら)構造体のコンストラクタを宣言する必要があることに注意してください。そうしないと、C++の不備のためmake_uniqueがコンパイルされません。

0
Alex K.

構造体subline_t定義済み、はい、常にコピーを返します。

0
Jacob