我有下面的数字向量模板类(用于数值计算的向量)。我正在尝试编写D=A+B+C
,其中所有变量都是Vector
个对象。不应修改A
,B
和C
。我的想法是使用Vector operator+(Vector&& B)
,以便在(有希望)从Vector
返回Rvalue B+C
之后,所有后续添加都存储在该对象中,即窃取所有Rvalue的存储空间随后的补充。这是为了消除新对象的创建和所需的存储。
我的问题是,我可以从每个被调用的函数的输出语句中看到,Vector operator+(Vector&& B)
永远不会被调用。我无法理解为什么如果我有一个重载的虚函数foo(Vector& B)
和foo(Vector&& B)
并尝试foo(A+B+C)
,那么第二个函数就像我希望的那样被调用。
很抱歉这个冗长的问题,但这是我在这里的第一个问题,我想尽量保持清醒。
对于我明显做错了什么或为什么我不应该这样做的任何建议,将不胜感激。
template <typename T>
class Vector
{
int n;
T* v;
Vector();
~Vector();
Vector(const Vector& B);
Vector(Vector&& B);
inline Vector operator+(const Vector& B) const;
inline Vector operator+(Vector&& B) const;
};
template <typename T>
Vector<T>::Vector(const Vector<T>& B)
{
...
}
template <typename T>
Vector<T>::Vector(Vector<T>&& B)
{
...
}
template <typename T>
Vector<T> Vector<T>::operator+(const Vector<T>& B) const
{
Vector<T> C;
...
return C;
}
template <typename T>
Vector<T> Vector<T>::operator+(Vector<T>&& B) const
{
...do stuff to B
return B;
}
答案 0 :(得分:7)
在表达式中:
D=A+B+C
A
和B
是左值,因此致电A+B
来电Vector::operator(const Vector&)
返回右值,我们称之为tmp
,因此下一个子表达式为tmp+C
。
C
也是左值,因此它再次调用Vector::operator(const Vector&)
。返回另一个右值,我们称之为tmp2
最终的子表达式为D=tmp2
,但您的类型没有移动赋值运算符,因此使用了隐式定义的复制赋值运算符。
即。你永远不会在右侧调用带有右值的operator+
,而唯一一个有rvalue参数的表达式是你没有为rvalues定义的赋值。
最好定义重载的非成员运算符:
Vector operator+(const Vector&, const Vector&);
Vector operator+(Vector&&, const Vector&);
Vector operator+(const Vector&, Vector&&);
Vector operator+(Vector&&, Vector&&);
这适用于rvalues和lvalues的任意组合。 (一般来说,operator+
通常应该是非成员。)
编辑:下面的替代建议不起作用,在某些情况下会导致含糊不清。
另一个替代方案,如果您的编译器支持它(我认为只有clang支持),那就是保留现有的Vector::operator+(Vector&&)
,但用两个由ref-qualifier区分的重载替换你的Vector::operator+(const Vector&)
:
Vector Vector::operator+(const Vector& v) const&
{
Vector tmp(*this);
tmp += v;
return tmp;
}
Vector Vector::operator+(const Vector& v)&&
{
*this += v;
return std::move(*this);
}
当已知它是一个右值时,它重用*this
,即当加法的左侧是右值时,它使用移动语义,与原始代码相比,原始代码只能在右侧使用移动语义 - 手边是右值。 (N.B.上面的代码假设您已根据David Rodriguez的回答中的建议定义了成员operator+=
)
答案 1 :(得分:1)
我建议你提供operator+=
作为成员方法,然后从定义为的自由函数operator+
中重用它:
template <typename T>
Vector<T> operator+( Vector<T> lhs, // by value
Vector<T> const & rhs ) {
lhs += rhs;
return lhs;
}
如果调用a + b + c
,其分组为(a+b) + c
,编译器将为a
的第一次调用创建operator+
的副本,并对其进行修改({ {1}})然后将其移动到返回的值中。然后它将移动返回的值(除非它忽略移动)进入第二个lhs += rhs
的参数,在那里它将再次被修改,然后移动返回值。
总的来说,将创建一个新对象,保存operator+
的结果,提供相当于的语义:
a+b+c
但是使用更好,更紧凑的语法Vector<T> tmp = a;
tmp += b;
tmp += c;
。
请注意,这不会优雅地处理a + b + c
,并且会创建两个对象,如果您想支持它,则需要产生多个重载。