【问题标题】:C++ Unable to move unique_ptr with universal referencesC++ 无法使用通用引用移动 unique_ptr
【发布时间】:2023-03-30 19:21:01
【问题描述】:

考虑这段代码:

template<typename T>
T mov(T&& t){
   return std::move(t);
}

int main(){
   std::unique_ptr<int> a = std::unique_ptr<int>(new int());
   std::unique_ptr<int> b = mov(a);
}

mov 函数应该简单地接受一个通用引用并按值返回它,但通过 moveing 它而不是复制它。因此,调用此方法时不应涉及复制。因此,使用只能移动的unique_ptr 调用这样的函数应该没问题。但是,此代码无法编译:我收到错误:

test.cpp:24:34: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
    std::unique_ptr<int> b = mov(a);

因此,C++ 似乎试图调用unique_ptrs 复制构造函数,这当然被删除了。但是为什么这里会出现副本呢?如何编译这段代码?

【问题讨论】:

  • std::unique_ptr&lt;int&gt; b = mov(std::move(a)); 有效应该是对问题的提示。
  • @WhozCraig:我特别想避免这里的move。否则,我不需要通用引用而是右值引用。通用引用的意义在于它也可以持有一个普通的左值引用。
  • 那我看不到结局了。你认为amov 没有按照我的评论移动的内容是什么? T&amp;&amp; 没有像您编写的那样提升为 xvalue。它需要被移动(或者我只是没有看到它,这不会是第一次)。
  • 就可以了。毫不奇怪,它与我的实现(clang 3.4)中的std::move 几乎相同。格拉茨!
  • 好的,我买那个。试图了解可能的用例是什么,直到该评论。谢谢!。

标签: c++ c++11 move-semantics unique-ptr universal-reference


【解决方案1】:

我相信错误在于mov的返回类型

代码应该是这样的

#include <utility>
#include <memory>
template<typename T>
typename std::remove_reference<T>::type&& mov(T&& t){
   return std::move(t);
}

int main(){
   std::unique_ptr<int> a = std::unique_ptr<int>(new int());
   auto b = mov(a);
}

这个问题暗示了按价值返回的场景,这也可以编译。我不确定它是否适用于您的情况;

template<typename T>
typename std::remove_reference<T>::type mov(T&& t){
   return std::move(t);
}

【讨论】:

  • 我在你之前就想到了同样的事情,但无论如何感谢你的回答:)
  • 解决方案对我来说很明显,对我来说不明显的是解释为什么原来的错误。
  • @gexicide 我现在看到了。同步打字。好小问题的想法。我认为参考崩溃会吸引人们一段时间。它已经咬了我好几次了。
  • @Niall:是的,确实很麻烦。我以为我已经理解了所有那些右值/移动语义的东西,但是到处都潜伏着隐藏的问题......
  • @gexicide 如果返回值是所需要的,typename std::remove_reference&lt;T&gt;::type mov(T&amp;&amp; t) 似乎也可以编译。
【解决方案2】:

我终于找到了一个可行的解决方案。我认为问题在于按值返回会触发副本。相反,我需要通过右值引用返回;然后将自动进行移动。首先我尝试了这个:

template<typename T>
T&& mov(T&& t){
   return std::move(t);
}

但是现在,问题在于返回类型T&amp;&amp; 是一个通用引用,而不是一个右值引用。因此,当使用左值调用函数时,实际签名是T&amp; mov(T&amp; t)。因此,它的主体将无法编译,因为我不能 std::move 到左值引用。这正是发生的事情,这是错误:

test.cpp:18:22: error: invalid initialization of non-const reference of type 

‘std::unique_ptr<int>&’ from an rvalue of type ‘std::remove_reference<std::unique_ptr<int>&>::type {aka std::unique_ptr<int>}’
    return std::move(t);

所以,我需要一个真正的右值引用作为返回类型。一开始我不知道怎么构造它,但最后我发现我首先需要std::remove_reference类型T然后添加&amp;&amp;,然后我将有一个真正的右值引用T&amp;&amp; .并且成功了,这个版本的mov 编译良好并解决了问题:

template<typename T>
typename std::remove_reference<T>::type&& mov(T&& t){
   return std::move(t);
}

正如 Niall 所说,通过引用返回也可以通过使用 remove_reference 而不使用 &amp;&amp;

template<typename T>
typename std::remove_reference<T>::type mov(T&& t){
   return std::move(t);
}

【讨论】:

  • 让返回类型typename std::remove_reference&lt;T&gt;::type 也可以工作吗?
  • @Mehrdad:是的,确实如此。但我不知道为什么
  • @Mehrdad 刚刚看到您的评论,同步输入...这里的按值返回在原始问题中有所暗示。
  • @gexicide:之所以有效,是因为移动构造发生在函数内部而不是外部,然后从右值输出产生的移动要么被忽略,要么被重新移动到外部变量中。
猜你喜欢
  • 1970-01-01
  • 2013-04-07
  • 2018-06-08
  • 1970-01-01
  • 2014-03-30
  • 1970-01-01
  • 2015-10-03
  • 2021-06-01
  • 1970-01-01
相关资源
最近更新 更多