假设我们只是简单地包装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"}) { .. }
您需要返回一个值:
C operator+(const C& rhs) && {
val += rhs.val;
return std::move(*this);
}
这样解决了这个问题(因为现在我们有临时扩展),如果移动你的对象比复制它们更便宜,那就是赢了。
C c = .... ; use_ctype(std::move(c));
?) - Kyle Strand