C++Rvalue引用和移动语义
本文关键字:语义 移动 引用 C++Rvalue | 更新日期: 2023-09-27 18:26:21
C++03存在可能隐式发生的不必要复制的问题。为此,C++11引入了rvalue references
和move semantics
。现在我的问题是,这种不必要的复制问题是否也存在于C#和java等语言中,或者它只是一个C++问题?换句话说,与C#或Java相比,rvalue references
是否使C++11更加高效?
就C#而言(允许运算符重载),假设我们有一个数学向量类,我们这样使用它。
vector_a = vector_b + vector_c;
编译器肯定会将vector_b + vector_c
转换为某个临时对象(让我们称之为vector_tmp
)。
现在我不认为C#可以区分像vector_tmp
这样的临时右值和像vector_b
这样的左值,所以我们无论如何都必须将数据复制到vector_a
,在C++11中使用rvalue references
和move semantics
可以很容易地避免这种情况。
shared_ptr
s的一些属性。然而,右值引用和移动语义更多地与临时值类型相关,但与C++值类型相比,C#中的值类型是非常不灵活的,根据我自己的C#经验,大多数时候你会得到类,而不是结构。
因此,我的假设是,Java和C#都不会从这些新的C++功能中获得太多利润,这些功能让代码可以安全地假设某个东西是否是临时的,而不是复制,让它只是窃取内容。
是的,C#和java中存在不必要的复制操作。与C#或Java相比,右值引用是否使C++11更加高效?
答案是肯定的
因为Java和C#中的类使用引用语义,所以在这些语言中从来没有对象的任何隐式副本。移动语义解决的问题在Java和C#中并不存在,也从未存在过。
我认为这可能发生在Java中。请参阅下面的add和add_to操作。add创建一个result对象来保存矩阵add操作的结果,而add_to只是将rhs添加到其中。
class Matrix {
public static final int w = 2;
public static final int h = 2;
public float [] data;
Matrix(float v)
{
data = new float[w*h];
for(int i=0; i<w*h; ++i)
{ data[i] = v; }
}
// Creates a new Matrix by adding this and rhs
public Matrix add(Matrix rhs)
{
Main result = new Main(0.0f);
for(int i=0; i<w*h; ++i)
{ result.data[i] = this.data[i] + rhs.data[i]; }
return result;
}
// Just adds the values in rhs to this
public Main add_to(Main rhs)
{
for(int i=0; i<w*h; ++i)
{ this.data[i] += rhs.data[i]; }
return this;
}
public static void main(String [] args)
{
Matrix m = new Matrix(0.0f);
Matrix n = new Matrix(1.0f);
Matrix o = new Matrix(1.0f);
// Chaining these ops would modify m
// Matrix result = m.add_to(n).subtract_from(o);
m.add_to(n); // Adds n to m
m.subtract_from(o); // Subtract o from n
// Can chain ops without modifying m,
// but temps created to hold results from each step
Matrix result = m.add(n).subtract(o);
}
}
因此,我认为这取决于你用你的类为用户提供什么样的功能。
这个问题经常出现。我想保留一个其他人无法修改的对象的唯一副本。我该怎么做?
- 对别人给我的任何东西都做一个深度复制?这是可行的,但效率不高
- 让人们给我一个新的对象,而不是保留副本?如果你勇敢的话,速度会更快。错误可能来自于一段完全无关的代码,该代码在数小时后修改了对象
- C++风格:将输入中的所有项目移动到我自己的新对象中。如果调用者不小心再次尝试使用该对象,他将立即看到问题
- 有时C#只读集合会有所帮助。但根据我的经验,这充其量只是一种痛苦
以下是我所说的:
class LongLivedObject
{
private Dictionary <string, string> _settings;
public LongLivedObject(Dictionary <string, string> settings)
{ // In C# this always duplicates the data structure and takes O(n) time.
// C++ will automatically try to decide if it could do a swap instead.
// C++ always lets you explicitly say you want to do the swap.
_settings = new Dictionary <string, string>(settings);
}
}
这个问题是Clojure和其他函数式语言的核心!
总之,是的,我经常希望我在C#中拥有C++11风格的数据结构和操作。
您可以尝试模拟移动语义。例如,在Trade Ideas Philip的示例中,您可以传递自定义MovableDictionary
而不是Dictionary
:
public class MovableDictionary<K, V> // : IDictionary<K, V>, IReadOnlyDictionary<K, V>...
{
private Dictionary<K, V> _map;
// Implement all Dictionary<T>'s methods by calling Map's ones.
public Dictionary<K, V> Move()
{
var result = Map;
_map = null;
return result;
}
private Dictionary<K, V> Map
{
get
{
if (_map == null)
_map = new Dictionary<K, V>();
return _map;
}
}
}