如何避免在C++中使用右值引用的不必要实例
How to avoid unnecessary instances using rvalue references in C++
我想创建一个自定义容器Container
,将数据存储在各个数组中。然而,为了便于在容器上进行简单的迭代,我通过重载operator[]
来提供容器的"视图",并返回一个结构Value
,该结构将所有容器变量作为对实际容器的引用。这就是我目前所得到的:
#include <iostream>
using namespace std;
struct Value {
Value(int& data) : data_(data) { }
int& data() { return data_; }
int& data_;
};
struct Container {
Value makeValue(int i) { return Value(data_[i]); } // EDIT 1
Value&& operator[](int i) {
// return std::forward<Value>(Value(data_[i]));
return std::forward<Value>(makeValue(i)); // EDIT 1
}
int data_[5] = {1, 2, 3, 4, 5};
};
int main(int, char**)
{
// Create and output temporary
Container c;
cout << c[2].data() << endl; // Output: 3 - OK!
// Create, modify and output copy
Value v = c[2];
cout << v.data() << endl; // Output: 3 - OK!
v.data() = 8;
cout << v.data() << endl; // Output: 8 - OK!
// Create and output reference
Value&& vv = c[2];
cout << vv.data() << endl; // Output: 8 - OK, but weird:
// shouldn't this be a dangling reference?
cout << vv.data() << endl; // Output: 468319288 - Bad, but that's expected...
}
据我所知,上面的代码是有效的,但我想知道我是否在这里使用了最好的方法:
- 如果我想避免不必要的复制,那么将
Value
作为右值引用返回是否正确 std::forward
的使用正确吗?我应该使用std::move
(在本例中两者都适用)还是其他什么- 已编译程序的输出在注释中说明。当我声明
Value&& vv...
(甚至在语法上禁止它)时,有什么方法可以避免悬挂引用吗
编辑1
我对源代码做了一个小的更改,使Value
实例不是直接在operator[]
方法中创建的,而是在另一个助手函数中创建的。这会改变什么吗?我应该使用如图所示的makeValue(int i)
方法吗?还是需要使用此处的std::move
/std::forward
?
如果我想避免不必要的复制,那么将Value作为右值引用返回是否正确?
没有。从std::move
或std::forward
这样的非辅助对象返回右值引用是完全错误的。Rvalue引用仍然是引用。返回对临时变量或局部变量的引用一直是错误的,现在仍然是错误的。这些都是旧的C++规则。
std::forward
的使用是否正确?我应该使用std::move
(在本例中两者都适用)还是其他什么?
上一个问题的答案让这个问题变得毫无意义。
已编译程序的输出在注释中说明。当我声明
Value&& vv...
(甚至在语法上禁止它)时,有什么方法可以避免悬挂引用吗?
创建悬空引用的不是Value&& vv = c[2];
部分。这是operator[]
本身:请参阅第一个问题的答案。
在这种情况下,Rvalue引用几乎没有变化。只要做你会一直做的事情:
Value operator[](int i) {
return Value(data_[i]);
}
任何值得使用的编译器都会将其优化为直接初始化返回值,而不需要任何副本或移动或任何东西。对于愚蠢/毫无价值/怪异/实验性的编译器,最坏的情况是需要移动(但为什么有人会在严肃的事情上使用这样的东西呢?)。
因此,线路Value v = c[2];
将直接初始化v
。行Value&& vv = c[2];
将初始化一个临时变量,并将其绑定到右值引用变量。它们具有与const&
相同的属性,并且它们将临时的生存期扩展到引用的生存期,因此它不会悬空。
总之,相同的旧C++仍然有效,并且仍然给出正确且性能良好的结果。别忘了。
返回对临时对象的引用,即使它是r值引用,也是错误的!当你访问这个对象时,它已经不见了。无论如何,在这种情况下,它也不会执行您希望它执行的操作:如果您想避免不必要的副本,请使用一个return语句返回一个临时的!复制/移动省略将处理未被复制的对象:
Value operator[](int i) {
return Value(data_[i]);
}
通过函数传递临时对象将禁止复制/移动省略,不复制/移动甚至比移动更少。
- 用callgrind追踪不必要的副本
- 不必要的C++代码最终会出现在我完成的程序中吗?
- 关于类的 Python 文档 - 对C++的引用不正确
- 总和的不必要行为C++?
- 引用不完整的类类型 - C++
- C++:将初始化的对象传递给另一个类的构造函数;需要不必要的构造函数吗?
- 类型为 "Bucket&"(未限定的 const 限定)的引用不能使用 "SortedList." 类型的值进行初始化 如何修复此错误?
- 在这种情况下,使用 string_view 是否会导致不必要的字符串复制?
- 为什么"std::uninitialized_copy"通常取消对未初始化内存的迭代器的引用不是未定
- 对"列表"的引用不明确,包括头文件
- std::mutex::lock() 产生奇怪(和不必要的)ASM 代码
- 为什么引用不能与编译时函数一起使用?
- 对临时对象的Const引用不会延长其生存期
- 如何在插入排序中使用 replace() 使语句变得不必要
- C 包装器C++库周围没有不必要的头文件
- 为什么要使用引用来获取 *char?当我们使用指针时,不使用引用不是更好吗?
- 编译器是否消除了不必要的原子?
- 在 c++ 中不必要的包含
- 处理对“vector_binary_operation”类中“表达式”的引用,而无需不必要的副本
- 如何避免在C++中使用右值引用的不必要实例