有没有一种很好的方法从C ++中的函数返回一个值,我们保证不会调用复制构造函数?返回值优化或移动构造函数都可以。例如,使用以下代码
#include <iostream>
struct Foo {
private:
// Disallow the copy and default constructor as well as the assignment
// operator
Foo();
Foo(Foo const & foo);
Foo & operator = (Foo const & foo);
public:
// Store a little bit of data
int data;
Foo(int const & data_) : data(data_) { }
// Write a move constructor
Foo(Foo && foo) {
std::cout << "Move constructor" << std::endl;
data=foo.data;
}
};
// Write a function that creates and returns a Foo
Foo Bar() {
Foo foo(3);
return foo;
}
// See if we can mix things up
Foo Baz(int x) {
Foo foo2(2);
Foo foo3(3);
return x>2 ? foo2 : foo3;
}
int main() {
// This is using the return value optimization (RVO)
Foo foo1 = Bar();
std::cout << foo1.data << std::endl;
// This should make the RVO fail
Foo foo2 = Baz(3);
std::cout << foo2.data << std::endl;
}
我们有编译错误
$ make
g++ -std=c++11 test01.cpp -o test01
test01.cpp: In function 'Foo Baz(int)':
test01.cpp:10:5: error: 'Foo::Foo(const Foo&)' is private
test01.cpp:35:25: error: within this context
make: *** [all] Error 1
因为复制构造函数是私有的。现在,如果我们将Baz函数修改为
// See if we can mix things up
Foo Baz(int x) {
Foo foo2(2);
Foo foo3(3);
return std::move(x>2 ? foo2 : foo3);
}
事实上,我们确实正常运行。但是,这似乎排除了RVO的使用。如果我们必须保证不调用复制构造函数,是否有更好的方法来构造这些函数?
答案 0 :(得分:3)
来自C ++标准:
[class.copy] / 31:当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数具有副作用。 ......在下列情况下(允许合并以消除多份副本),允许复制/移动操作(称为复制省略)的省略:
- 在具有类返回类型的函数的return语句中,当表达式是a的名称时 非易失性自动对象(函数或catch子句参数除外)具有相同的cv- 作为函数返回类型的非限定类型,可以通过构造省略复制/移动操作 自动对象直接进入函数的返回值
由于x > 2 ? foo2 : foo3
不是自动对象的名称,因此不允许复制省略。
答案 1 :(得分:1)
有趣的是,您的示例已在n1377中解决:
有了这种语言功能,移动/复制省略,尽管仍然如此 重要的是,不再是关键。 NRVO有一些功能 允许,但可能非常难以实施。对于 例如:
A f(bool b) { A a1, a2; // ... return b ? a1 : a2; }
介于难以决定是否存在之间 在呼叫者的首选位置构造a1或a2。使用A的举动 构造函数(而不是复制构造函数)将a1或a2发送回 来电是最好的解决方案。
我们可以要求operator +的作者明确请求 移动语义。但重点是什么?目前的语言 已经允许这个副本的省略,所以编码器已经可以了 不依赖于当地的破坏秩序,也不能依靠它 复制构造函数被调用。自动本地即将到来 无论如何,它在概念上被破坏了,所以它非常像“rvalue-like”。此举 除非通过测量性能或计算副本,否则无法检测到 (无论如何都可以省略。)
请注意,此语言添加允许移动,但不可复制 自移动以来,由值返回的对象(例如move_ptr) 找到并使用(或省略)构造函数而不是不可访问的构造函数 复制构造函数。
他们解决这个问题的例子(支持移动语义)是:
// Or just call std::move
// return x>2 ? static_cast<Foo&&>(foo2) : static_cast<Foo&&>(foo3);
return static_cast<Foo&&>(x>2 ? foo2 : foo3);
这种隐式演员产生的逻辑导致自动 “移动语义”从最好到最差的层次结构:
If you can elide the move/copy, do so (by present language rules) Else if there is a move constructor, use it Else if there is a copy constructor, use it Else the program is ill formed
或者正如Xeo所提到的,你可以这样构建它:
Foo Baz(int x) {
Foo foo2(2);
Foo foo3(3);
if (x > 2)
return foo2;
else
return foo3;
}
您已经在OP中提供了一个示例,但该标准提供了一个用于删除移动/复制构造函数的示例(同样适用):
class Thing {
public:
Thing() { }
~Thing() { }
Thing(Thing&& thing) {
std::cout << "hi there";
}
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
// does not print "hi there"
但是如果同时提供移动和复制构造函数,那么移动构造函数似乎是首选。