繁体   English   中英

为什么返回右值引用错误

[英]why is returning rvalue reference wrong

我想知道具有r引用返回类型的地方出了什么问题:

vector<T>&& id2(vector<T>&& v) {  return std::move(v);}

如下面的代码。 a = id2(a)设置为空。 返回正常类型的另一个版本id可以。

我听说这与自主工作有关 但是我仅具有关于移动语义的基本知识,并且我不理解标准中引用的答案中的神秘语言。 有人可以用常识/外行的语言解释一下吗?

如果我把一个临时a ,获得了其内容,并返回内容作为临时被分配回a ,为什么要a被清除或不确定的?

#include <vector>
#include <iostream>
using namespace std;

template <class T>
vector<T> id(vector<T>&& v) {  return std::move(v); }

template <class T>
vector<T>&& id2(vector<T>&& v) {  return std::move(v);}

int main() {
  vector<int> a;
  a.resize(3,1);
  a = id(std::move(a));
  cout << a.size() << endl;  //output: 3

  a.resize(3,1);
  a = id2(std::move(a));
  cout << a.size() << endl;  //output: 0
}

谢谢,

-更新-

如果我正在阅读上述重复帖子 ,则下面引用的第二个示例是关于将r-ref传递到函数外部的局部变量,无论这是r-ref还是l-ref,无论如何都是错误的。 我认为这与我的示例不太相同。 如果在我的示例中使用左引用而不是r-ref,它会起作用。 我在这方面缺少什么吗?

std::vector<int>&& return_vector(void)
{
    std::vector<int> tmp {1,2,3,4,5};
    return std::move(tmp);
}

std::vector<int> &&rval_ref = return_vector();

-更新2-

@pradhan。 看来问题归结为自我完成任务。 关于标准容器自移动分配的行为库选择,这让我感到困扰。 如果您以旧的方式对std::vector进行自我分配,那就很好了,但是当您自我移动分配时,这是未定义的行为。 除非存在本质上的问题,否则让标准容器允许自移动分配和自分配一样更自然(向后兼容)?

#include <vector>
#include <iostream>

int main() {
  std::vector<int>a{1,2,3};
  a = a;
  std::cout << a.size() << std::endl;  //output: 3
  a = std::move(a);
  std::cout << a.size() << std::endl;  //output: 0
}

您的函数id2不执行任何操作,代码等效于

a = std::move(a);

您所链接的Howard Hinnant的回答解释说,这本身是不允许的。 用外行的话来说:该标准不需要支持自动移动分配。 因此,不需要移动分配运算符的实现来执行自赋值的测试,并且结果为未定义行为。

如果您使a=std::move(a)合法,那么它什么也不会做。 问题在于,这需要进行检查,这对于非自我移动分配情况是多余的,因此对所有这些分配都进行了悲观评估。

您违反了rvalue引用不是alias的约定(这是调用库函数的要求)。 问题中提到的霍华德·辛南特(Howard Hinnant)的答案包含标准文字和自动执行的应用程序。 这是实际的结果:

将移动分配实现为:

T& T::operator=(T&& other)
{
    clear();
    swap(*this, other);
    return *this;
}

或更安全的异常变体:

T& T::operator=(T&& other)
{
    swap(*this, other);
    other.clear();
    return *this;
}

只要遵守别名惯例,这也是安全的。

很明显,如果您使用别名,为什么这两种实现方式都会产生一个空对象。

顺便说一句, id2返回一个右值引用没有什么问题。 问题在于a = id2(a); 它使用右值引用为同一表达式中的另一个对象引用加上别名。

您问题的链接和Hinnant的答案都处理了标准对标准库的实现提出的要求。 a=std::move(a)本质上没有错。 如果a是类类型T ,则a=std::move(a)取决于T的实现。 该标准允许(标准)库实现在给定右值引用参数时做出唯一的引用假设。 a具有T=vector<U>a=std::move(a)违反唯一参考假设。 由于您违反了图书馆合同,因此产生了UB。 但是,您可以拥有自己的类,该类的move构造函数实现具有artst a=std::move(a)的完美定义的行为。 该标准通常没有提及自移动分配。 它仅指定标准库实现者需要支持什么,以及如果他们愿意的话,他们可以选择不选择什么。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM