return 通过移动后的右值引用
return by rvalue reference after move
我对以下示例有几个问题:
unique_ptr<A> foo(){
unique_ptr<A> a = make_unique<A>(5);
return move(a);
}
unique_ptr<A>&& foo(){
unique_ptr<A> a = make_unique<A>(5);
return move(a);
}
unique_ptr<int> foo(){
unique_ptr<int> a = make_unique<int>(5);
return a;
}
第一个例子:
为什么编译器允许这样的事情?我们不是 return 右值引用吗?为什么编译器允许这样的隐式转换?谁持有底层对象?最后谁负责销毁它?
第二个例子:
当我这样做时,我从 return 类型的函数中得到了垃圾。虽然我认为这是执行此操作的 "right" 方法,但我们不是在声明我们正在 return 一个 rval 引用,并且实际上是这样移动我们的对象吗?
第三个例子:
如果删除了unique_ptr
的拷贝构造函数,这里会发生什么情况允许这个函数? a
是一个 unique_ptr
,我们按值 return 计算它,所以不会创建一个副本吗?
这是一个快速 运行-down:
第一个例子还可以,但是 std::move(a)
抑制了 cooy-elision。当然,移动比复制更好,但没有工作更好。
右值引用仍然是引用,您需要保持引用对象的活动。在可以访问 returned 引用之前,引用的本地对象被销毁。 return 右值引用很少有用,尽管一些标准函数这样做(std::move(x)
、std::forward<T>(x)
、std::declval<T>()
)但是对于这些有一个非本地对象 returned.
当复制省略成为可能时,对象被隐式移动。这就是复制省略仍然可行的方法。
您似乎没有意识到复制省略:在某些需要复制的情况下,即使程序的语义发生了变化,编译器也可以省略复制(即,不会发生复制 ctor 和 dtor 的副作用)。实际上,复制省略允许立即在正确的位置构造对象。只有少数情况允许复制省略(具体规则更复杂):
- 复制临时对象时。
- 当 returning 局部变量时。
- 抛出局部变量时。
- 重新抛出捕获的对象时。
由于在这些情况下没有进行任何复制,因为对象已经在正确的位置,因此将规则扩展到移动操作似乎是合理的:当允许复制省略时,对象被隐式移动。最终效果是拥有复制或移动构造函数就足够了。任何像样的编译器既不会复制也不会移动,但会省略 copy/move 结构。
我对以下示例有几个问题:
unique_ptr<A> foo(){
unique_ptr<A> a = make_unique<A>(5);
return move(a);
}
unique_ptr<A>&& foo(){
unique_ptr<A> a = make_unique<A>(5);
return move(a);
}
unique_ptr<int> foo(){
unique_ptr<int> a = make_unique<int>(5);
return a;
}
第一个例子:
为什么编译器允许这样的事情?我们不是 return 右值引用吗?为什么编译器允许这样的隐式转换?谁持有底层对象?最后谁负责销毁它?
第二个例子:
当我这样做时,我从 return 类型的函数中得到了垃圾。虽然我认为这是执行此操作的 "right" 方法,但我们不是在声明我们正在 return 一个 rval 引用,并且实际上是这样移动我们的对象吗?
第三个例子:
如果删除了unique_ptr
的拷贝构造函数,这里会发生什么情况允许这个函数? a
是一个 unique_ptr
,我们按值 return 计算它,所以不会创建一个副本吗?
这是一个快速 运行-down:
第一个例子还可以,但是
std::move(a)
抑制了 cooy-elision。当然,移动比复制更好,但没有工作更好。右值引用仍然是引用,您需要保持引用对象的活动。在可以访问 returned 引用之前,引用的本地对象被销毁。 return 右值引用很少有用,尽管一些标准函数这样做(
std::move(x)
、std::forward<T>(x)
、std::declval<T>()
)但是对于这些有一个非本地对象 returned.当复制省略成为可能时,对象被隐式移动。这就是复制省略仍然可行的方法。
您似乎没有意识到复制省略:在某些需要复制的情况下,即使程序的语义发生了变化,编译器也可以省略复制(即,不会发生复制 ctor 和 dtor 的副作用)。实际上,复制省略允许立即在正确的位置构造对象。只有少数情况允许复制省略(具体规则更复杂):
- 复制临时对象时。
- 当 returning 局部变量时。
- 抛出局部变量时。
- 重新抛出捕获的对象时。
由于在这些情况下没有进行任何复制,因为对象已经在正确的位置,因此将规则扩展到移动操作似乎是合理的:当允许复制省略时,对象被隐式移动。最终效果是拥有复制或移动构造函数就足够了。任何像样的编译器既不会复制也不会移动,但会省略 copy/move 结构。