我想知道有一个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
}
答案 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)
。一般来说,标准并没有说明任何关于自动分配的事情。它只指定了标准库实现者需要支持的内容以及他们可以选择的内容,如果它们满意的话。