所以..我正在编写一些代码,想知道复合运算符的原型(即operator*=
或operator/=
之类的东西)。当我查看它时,我意识到他们应该返回引用。 (或者至少我的消息来源指示如下:R& operator +=(K& a, S b);
。)嗯..然后我意识到我的代码中的一行可能比它看起来更危险:
// I have a really fancy Vector class I've been making. :P
template<typename T, int N>
inline Vector<T, N> operator*(const Vector<T, N>& vec, T scale) {
Vector<T, N> ret = vec;
return ret *= scale;
}
所以..我想知道这是否无害......或者会导致对局部变量的引用泄漏并导致各种未定义的行为和一般的破坏。 (我倾向于破坏,因此,重写如下。:P)
// I have a really fancy Vector class I've been making. :P
template<typename T, int N>
inline Vector<T, N> operator*(const Vector<T, N>& vec, T scale) {
Vector<T, N> ret = vec;
ret *= scale;
return ret;
}
所以..是啊..一般C ++&#34;如果?&#34;这里有问题。很高兴知道肯定。 (而且我太懒了,不能尝试做一个测试用例,看看我的程序是否停止并起火了。:P)
编辑:修复上面的代码后..我意识到将这个复合运算符放在这里可能会有所帮助。 :P
template<typename T, int N>
inline Vector<T, N>& operator*=(Vector<T, N>& vec, T scale) {
for (int i = 0; i < N; i++) {
vec[i] *= scale;
}
return vec;
}
所以...修改了代码(并经过双重检查)我想知道使用第一个变体是否仍然会引起悬空引用(因为operator*=
的返回类型是引用)
答案 0 :(得分:5)
按照惯例,像*=
这样的复合赋值运算符应该返回对它们修改的对象的引用,因为没有理由进行不必要的复制。也就是说,即使在你修改过的例子中,你也会得到一个“悬空”引用,因为你仍然会返回一个对函数返回后将被销毁的局部变量的引用。您应该按值而不是按引用返回。
template <typename T, int N>
Vector<T, N> operator*(Vector<T, N> const& vec, T scale) {
Vector<T, N> ret = vec;
return ret *= scale;
}
另请注意,如果按值传递ret
,则可以删除vec
。如果可以更有效地移动Vector<T, N>
,则可以允许更高效的客户端代码。
template <typename T, int N>
Vector<T, N> operator*(Vector<T, N> vec, T scale) {
return vec *= scale;
}
答案 1 :(得分:1)
(虽然@ JosephThomson在下面的回答“回答”了这个问题,但它并没有像我认为的那样简单,所以我在这里提供答案。)
template<typename T, int N>
inline Vector<T, N> operator*(const Vector<T, N>& vec, T scale) {
Vector<T, N> ret = vec;
return ret *= scale;
}
上述中的return ret *= scale;
不会导致悬空参考错误。原因是返回类型为Vector<T, N>
且不为引用类型。因此,即使将operator*=
定义为返回引用类型,也会在operator*
返回时生成副本(有效地剥离引用)。
答案 2 :(得分:0)
您有两种选择:
如果你在类中定义运算符,它需要一个参数,你修改类数据并返回* this而不返回局部变量:
R& K::operator *=(S b);
如果在类外定义运算符,则需要两个参数,然后修改该参数并返回该参数而不返回局部变量:
R& operator *=(K& a, S b);
在Canonical Implementations下的http://en.cppreference.com/w/cpp/language/operators,他们有这个例子:
class X
{
public:
X& operator+=(const X& rhs) // compound assignment (does not need to be a member,
{ // but often is, to modify the private members)
/* addition of rhs to *this takes place here */
return *this; // return the result by reference
}
// friends defined inside class body are inline and are hidden from non-ADL lookup
friend X operator+(X lhs, // passing lhs by value helps optimize chained a+b+c
const X& rhs) // otherwise, both parameters may be const references
{
lhs += rhs; // reuse compound assignment
return lhs; // return the result by value (uses move constructor)
}
};