3

考虑以下代码:

struct Foo
{
    Foo() { cout << "Foo()\n"; }
    ~Foo() { cout << "~Foo()\n"; }
    Foo(Foo&) { cout << "Foo(Foo&)\n"; }
    Foo(Foo&&) { cout << "Foo(Foo&&)\n"; }

    int d;
};

struct Bar
{
    Foo bigData;
    void workOnBigData() { /*...*/ }
}

Foo getBigData()
{
    Bar b;
    b.workOnBigData();
    return b.bigData;
}

getBigData()就复制省略/移动语义而言,实现的最佳方法是什么?在这个实现中,编译器似乎不允许移动bigData。我测试了以下功能:

Foo f()
{
    Foo foo;
    return foo;  // RVO
}

Foo g()
{
    Bar b;
    return b.bigData;  // Copy
}

Foo h()
{
    Bar b;
    auto r = move(b.bigData);
    return r;  // Move
}

你能解释一下这些实现的结果,并展示返回本地对象成员的最有效方法吗?

4

2 回答 2

3

有很多方法可以避免额外的副本,更接近您的代码的一种方法是恕我直言:

Foo getBigData()
{
    Foo ret; // do a cheap initialization
    Bar b;
    b.workOnBigData();

    std::swap(ret, b.bigData); // 'steal' the member here

    return ret; // NRVO can apply
}

同样可以通过 move 构造返回对象来实现

Foo getBigData()
{
    Bar b;
    b.workOnBigData();     
    Foo ret(std::move(b.bigData)); // these two lines are equivalent to
    return ret;                    // return std::move(b.bigData); 
}
于 2015-07-20T08:31:34.313 回答
1

我想回答这个问题为什么这里没有省略复制构造函数?在回答您的问题时非常有用。您的示例中未使用复制省略

Foo getBigData()
{
    Bar b;
    b.workOnBigData();
    return b.bigData;
}

由于此要求未完全满足(http://en.cppreference.com/w/cpp/language/copy_elision):

return 语句的表达式是具有自动存储持续时间的非易失性对象的名称......并且与函数的返回类型具有相同的类型(忽略顶级 cv 限定),然后省略复制/移动

在您的示例中,Bar 是一个具有自动存储持续时间的变量,但您返回 Foo。如果您更改 Bar 类,编译器将开始使用复制省略:

#include <iostream>
#include <typeinfo>

using namespace std;

struct Foo
{
  Foo() { cout << "Foo()\n"; }
  ~Foo() { cout << "~Foo()\n"; }
  Foo(const Foo&) { cout << "Foo(Foo&)\n"; }
  Foo(Foo&&) { cout << "Foo(Foo&&)\n"; }

  int d;
};

struct Bar
{
  Foo bigData;
  void workOnBigData() { /*...*/ }
};

struct Bar2
{
  void workOnBigData(Foo&) { /*...*/ }
};

Foo getBigData()
{
    Bar b;
    b.workOnBigData();
    return b.bigData;
}

Foo getBigData2()
{
    Foo f;
    Bar2 b;
    b.workOnBigData(f);
    return f;
}

int main()
{
  {
    Foo f = getBigData();
  }
 cout << "---" << endl;

  {
    Foo f = getBigData2();
  }
}

#include <iostream>
#include <typeinfo>

using namespace std;

struct Foo
{
  Foo() { cout << "Foo()\n"; }
  ~Foo() { cout << "~Foo()\n"; }
  Foo(const Foo&) { cout << "Foo(Foo&)\n"; }
  Foo(Foo&&) { cout << "Foo(Foo&&)\n"; }

  int d;
};

struct Bar
{
  Foo bigData;
  void workOnBigData() { /*...*/ }
};

struct Bar2
{
  void workOnBigData(Foo&) { /*...*/ }
};

Foo getBigData()
{
    Bar b;
    b.workOnBigData();
    return b.bigData;
}

Foo getBigData2()
{
    Foo f;
    Bar2 b;
    b.workOnBigData(f);
    return f;
}

int main()
{
  {
    Foo f = getBigData();
  }
 cout << "---" << endl;

  {
    Foo f = getBigData2();
  }
}

这是它的输出:

$ ./a.out     
Foo()
Foo(Foo&)
~Foo()
~Foo()
---
Foo()
~Foo()
于 2015-07-20T15:50:45.370 回答