[英]C++ how is it technically possible to move a function return value without a copy?
When a function returns a value, this is put on the stack (the function stack frames are deleted, but the return value remains there until the caller gets it). 当一个函数返回一个值时,它被放到栈上(删除函数栈帧,但是返回值一直保留在那里直到调用者得到它为止)。
If the return value is on the stack how can the move get that value without copying it in the variable memory location? 如果返回值在堆栈上,那么移动如何在不将其复制到变量存储位置的情况下获取该值?
For example in this code: 例如下面的代码:
A a = getA();
The storage (Heap, stack, registers, etc) used to store the temporary used to return values from a function is implementation defined. 实现定义了用于存储用于从函数返回值的临时存储的存储(堆,堆栈,寄存器等)。 You could see it as:
您可以将其视为:
+-----------------------------+-------------------------+-----------------------------------------+
| target (caller stack frame) | temporary (unspecified) | return statement (function stack frame) |
+-----------------------------+-------------------------+-----------------------------------------+
The value is passed from the right to the left. 该值从右向左传递。 Also note that the standard specifies that any compiler could elide the temporary and the assigments/copies/moves and directly initialize the target.
还要注意,该标准指定任何编译器都可以忽略临时文件和分配/副本/动作,并直接初始化目标。
Writting a class such as: 编写一个类,例如:
class trace
{
public:
trace()
{
std::cout << "Init" << std::endl;
}
~trace()
{
std::cout << "Destroy" << std::endl;
}
trace( const trace& )
{
std::cout << "Copy init" << std::endl;
}
trace( trace&& )
{
std::cout << "Move init" << std::endl;
}
trace& operator=( const trace& )
{
std::cout << "Copy assign" << std::endl;
}
trace& operator=( trace&& )
{
std::cout << "Move assign" << std::endl;
}
};
And trying it with different compiler optimizations enabled is very ilustrative. 在启用了不同的编译器优化的情况下进行尝试非常有启发性。
In many implementations of C++, a function returning a "complex" data type is passed a hidden parameter that is a pointer to the space where the returned instance is to reside. 在C ++的许多实现中,将向返回“复杂”数据类型的函数传递一个隐藏参数,该参数是指向要返回的实例所驻留的空间的指针。 Essentially, the compiler turns
Foo r = fun();
本质上,编译器将
Foo r = fun();
into 进入
char alignas(Foo) r[sizeof Foo]; // Foo-sized buffer, unitialized!
fun(&r);
As you can see, Foo
is allocated on the stack in the caller's frame. 如您所见,
Foo
被分配在调用者框架中的堆栈上。 Now, within the implementation of fun
there could be a copy. 现在,在执行
fun
中可能会有一个副本。 The construction 那个工程
Foo fun() {
Foo rv;
...
return rv;
}
is generally implemented as 通常实现为
void fun(Foo * $ret) {
Foo rv;
..
new ($ret) Foo(rv); // copy construction
}
When the return value optimizations are applied, this gets changed to 当应用返回值优化时,将其更改为
void fun(Foo * $ret) {
Foo & rv = *(new ($ret) Foo);
...
return;
}
Now there's no copying involved. 现在不涉及复制。 That's a mile-high overview of how an implementation might do it.
这是实现可能如何实现的概述。
You're assuming that A is an aggregate or primitive. 您假设A是一个集合或原始值。 If this is true then yes move and copy semantics are equivalent.
如果为真,则“是”移动和复制语义是等效的。 If however A is a complex type like vector then it will contain pointers to resources.
但是,如果A是像vector这样的复杂类型,则它将包含指向资源的指针。 When moving the object the pointers are copied without copying the value they point to.
移动对象时,将复制指针而不复制其指向的值。
When you say "move", I assuming you're referring to C++11 move constructors and the std::move
function. 当您说“ move”时,我假设您指的是C ++ 11 move构造函数和
std::move
函数。
Moving an object doesn't actually move the entire object. 移动对象实际上并不会移动整个对象。 It constructs a new one using its move constructor, which is allowed to take ownership of resources held by the original object instead of copying them.
它使用其move构造函数构造一个新对象,该构造函数允许获取原始对象持有的资源的所有权,而不是复制它们。 For example, if you write:
例如,如果您编写:
std::vector<int> foo = function_that_returns_a_vector();
the compiler may implement this by calling foo
's move constructor and passing it the temporary vector returned by the function. 编译器可以通过调用
foo
的move构造函数并向其传递函数返回的临时向量来实现此目的。 The move constructor will take ownership of the temporary vector's internal pointer to its heap-allocated contents, leaving the temporary vector empty. move构造函数将使用临时向量的内部指针对其堆分配的内容的所有权,而使临时向量为空。 Prior to C++11 and move support,
foo
's copy constructor would've been called, which would've allocated new space on the heap to copy the returned vector's contents even though that returned vector is about to be destroyed and won't need its own copy any longer. 在C ++ 11和移动支持之前,将调用
foo
的copy构造函数,该函数将在堆上分配新的空间以复制返回的向量的内容,即使该返回的向量将要销毁并获得胜利也是如此。不再需要自己的副本。
Note that the compiler won't necessarily implement that line by constructing foo
from a returned temporary at all, though. 请注意,尽管如此,编译器不一定会通过从返回的临时结构构造
foo
来实现该行。 Depending on the compiler's platform-specific calling convention, the address of the (uninitialized) foo
variable may be passed into the function in such a way that the function's return value is constructed directly into foo
, avoiding the need for a copy after the function returns. 根据编译器特定于平台的调用约定,(未初始化的)
foo
变量的地址可以通过以下方式传递到函数中:将函数的返回值直接构造为foo
,从而避免在函数返回后进行复制。 This is called copy elision . 这称为复制省略 。
The easiest is to modify your method like this: 最简单的方法是像这样修改您的方法:
void getA(A& out);
A a;
getA(a);
However, your compiler does it's best to avoid such superfluity: Copy Elision 但是,您的编译器确实最好避免这种多余的情况: Copy Elision
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.