为什么返回rvalue引用错误

时间:2014-07-07 20:27:30

标签: c++ c++11 move-semantics

我想知道有一个r-reference返回类型出现了什么问题:

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

,如下面的代码所示。执行a = id2(a)设置为空。返回正常类型的另一个版本id是正常的。

我听说这与self-move assignment有关。但我只掌握有关移动语义的基本知识,而且我不理解标准中引用的答案中的含义语言。有人可以用常识/外行的语言来解释这个吗?

如果我采用临时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
}

谢谢,

- 更新---

如果我正在阅读said duplicate post,下面引用的第二个例子是关于将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
}

3 个答案:

答案 0 :(得分:2)

您的函数id2什么都不做,代码等同于

a = std::move(a);

你联系的Howard Hinnant的回答解释说这本身是不允许的。通俗地说:该标准不要求支持自动分配。因此,不需要执行移动赋值运算符来执行自我赋值的测试,结果就是未定义的行为。

如果您要使a=std::move(a)合法,那么 。问题在于,这需要对非自动分配案例进行多余的检查,因此对所有这些分配感到悲观。

答案 1 :(得分:2)

您违反了约定(这是调用库函数的要求)右值引用不是别名。问题中提到的Howard Hinnant的answer包含标准文本和自动移动应用程序。这是实际结果:

将移动分配实施为:

,这既合法又通用
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);使用了一个右值引用,该引用将同一个表达式中的另一个对象引用别名。

答案 2 :(得分:0)

您的问题和Hinnant的答案中的链接都涉及标准对标准库实现的要求。 a=std::move(a)没有任何内在错误。如果a属于类型T,则a=std::move(a)的结果取决于T的实现。该标准允许(标准)库实现在给定rvalue引用参数时进行唯一引用假设。当a类型为T=vector<U>时,a=std::move(a)会违反唯一引用假设。由于您违反了图书馆合同,因此会产生UB。但是,您可以拥有自己的类,其移动构造函数实现具有完全明确定义的行为a=std::move(a)。一般来说,标准并没有说明任何关于自动分配的事情。它只指定了标准库实现者需要支持的内容以及他们可以选择的内容,如果它们满意的话。