通过 ref-qualifier 重载操作符以防止临时操作是否合理?
Is it reasonable to overload operators by ref-qualifier to prevent temporaries?
我刚想到 operator+
& co 可以对 this
进行右值运算;即给定 class C
,可以这样做:
class C {
// ...
C operator-( const C& rhs ) const & {
C result = *this;
result -= rhs;
return result;
}
C&& operator-( const C& rhs ) && {
*this -= rhs;
return std::move( *this );
}
};
这将通过简单地就地修改临时值来防止复制。
这会像我预期的那样执行吗?这是合理的优化还是编译器会创建同样快速的代码?
假设我们只是包装 std::string
并做 operator+
的简化版本:
struct C {
std::string val;
C&& operator+(const C& rhs) && {
val += rhs.val;
return std::move(*this);
}
std::string::iterator begin() { return val.begin(); }
std::string::iterator end() { return val.end(); }
};
这样就可以正常工作了:
for (char c : C{"hello"}) { .. }
range-for 表达式将延长临时对象的生命周期,所以我们没问题。但是,考虑一下:
for (char c : C{"hello"} + C{"goodbye"}) { .. }
我们实际上有:
auto&& __range = C{"hello"}.operator+(C{"goodbye"});
在这里,我们没有将临时对象绑定到引用。我们正在绑定一个参考。该对象不会延长其生命周期,因为...它不是对象。所以我们有一个悬空引用和未定义的行为。对于希望它起作用的用户来说,这将是非常令人惊讶的:
for (char c : std::string{"hello"} + std::string{"goodbye"}) { .. }
您必须 return 一个值:
C operator+(const C& rhs) && {
val += rhs.val;
return std::move(*this);
}
这解决了这个问题(因为现在我们有临时扩展),如果移动你的对象比复制它们更便宜,这是一个胜利。
我刚想到 operator+
& co 可以对 this
进行右值运算;即给定 class C
,可以这样做:
class C {
// ...
C operator-( const C& rhs ) const & {
C result = *this;
result -= rhs;
return result;
}
C&& operator-( const C& rhs ) && {
*this -= rhs;
return std::move( *this );
}
};
这将通过简单地就地修改临时值来防止复制。
这会像我预期的那样执行吗?这是合理的优化还是编译器会创建同样快速的代码?
假设我们只是包装 std::string
并做 operator+
的简化版本:
struct C {
std::string val;
C&& operator+(const C& rhs) && {
val += rhs.val;
return std::move(*this);
}
std::string::iterator begin() { return val.begin(); }
std::string::iterator end() { return val.end(); }
};
这样就可以正常工作了:
for (char c : C{"hello"}) { .. }
range-for 表达式将延长临时对象的生命周期,所以我们没问题。但是,考虑一下:
for (char c : C{"hello"} + C{"goodbye"}) { .. }
我们实际上有:
auto&& __range = C{"hello"}.operator+(C{"goodbye"});
在这里,我们没有将临时对象绑定到引用。我们正在绑定一个参考。该对象不会延长其生命周期,因为...它不是对象。所以我们有一个悬空引用和未定义的行为。对于希望它起作用的用户来说,这将是非常令人惊讶的:
for (char c : std::string{"hello"} + std::string{"goodbye"}) { .. }
您必须 return 一个值:
C operator+(const C& rhs) && {
val += rhs.val;
return std::move(*this);
}
这解决了这个问题(因为现在我们有临时扩展),如果移动你的对象比复制它们更便宜,这是一个胜利。