为什么返回右值引用是错误的
why is returning rvalue reference wrong
我想知道有一个r-引用返回类型出了什么问题:
vector<T>&& id2(vector<T>&& v) { return std::move(v);}
,如下面的代码所示。使a = id2(a)
集为空。另一个版本id
返回正常类型为OK。
我听说这与自移动赋值有关。但我对移动语义只有基本的了解,而且我不理解从标准中引用的答案中的隐晦语言。有人能用常识/外行的语言解释一下吗?
如果我取一个临时的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
}
谢谢,
—Update—
如果我正在阅读上述重复的帖子,下面引用的第二个例子是关于将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();
—Update 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)
合法,它将不做任何事情。问题是,这需要检查,这对于非自移动赋值情况是多余的,因此悲观所有这些赋值。
您违反了右值引用不别名的约定(这是调用库函数的要求)。问题中提到的Howard Hinnant的答案包含标准文本和自我移动的应用程序。下面是实际结果:
将move-assignment实现为:
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构造函数实现在wrt a=std::move(a)
中具有完美定义的行为。标准没有说任何关于自移动赋值的事情。它只指定标准库实现者需要支持什么,以及他们可以选择不支持什么,如果他们愿意的话。
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- 对C宏的未定义引用,但在定义它时会出现重新定义错误
- C++引用错误
- 已定义函数时出现 G++ "未定义的引用"错误
- 错误:使用通用引用的声明冲突
- 链接 cmake 时出现未定义的引用错误
- 错误:未定义对'oboe::AudioStreamBuilder::openStream(oboe::AudioStream**)'的引用
- 链接器错误:未定义对"Reference_Genome::seq[abi:cxx11]"的引用
- C++ 中的编译错误:未定义对"主"的引用 collect2:错误:ld 返回 1 个退出状态
- 错误:无法解析对重载函数的引用;你的意思是调用它吗?
- 在 gtest 中初始化堆栈上的引用变量的隔离错误
- 运行时错误:引用绑定到类型为"int"的空指针
- 隐式重新解释引用时强制转换,没有警告/错误
- 只有级联分类器会发出未定义的引用错误
- 错误:未定义对cv::cudacodec::createVideoReader的引用
- 模板流运算符重载错误:引用初始化无效,与basic_istream和basic_ifstream之间的差异有关
- 对于我的 ComplexNumber 中的某些方法,我得到了一个奇怪的未定义错误引用.cpp,不过我对模板很陌生
- 从对象调用成员对象,错误:引用非常量值的初始值必须是左值
- Qt并发错误:引用非静态成员