通过引用限定符重载运算符以防止临时对象,这样做合理吗?

6

我突然想到operator+等可以对rvalue的this进行操作;也就是说,对于一个类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 );
    }
};

这将防止通过简单地就地修改临时值来复制。

这样做是否符合我的预期?这是一个合理的优化还是编译器会创建同样快的代码?


通过第一个版本,您已经可以获得复制省略。因此,我不确定是否需要第二个版本(如果它真的有效,我觉得它会让你后悔,但我可能错了)。 - Borgleader
是的,我有一种感觉,拷贝省略可能会带来类似的性能优化... 但我还没看出我的版本会有什么问题。(我不确定,所以才问。) - Mr. Wonko
您似乎是对的。我还不太理解为什么。 - Mr. Wonko
@T.C. 你可以声明对象为rvalue类型吗?为什么?这对任何事情有用吗?(例如,为什么不只是C c = .... ; use_ctype(std::move(c));?) - Kyle Strand
1
@KyleStrand 如果临时变量是非可移动类型,则将其悬挂在空中并延长其生命周期。你很少需要明确使用它,但范围-based for 循环会在内部执行此操作。 - T.C.
显示剩余2条评论
1个回答

5

假设我们只是简单地包装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);
}

这样解决了这个问题(因为现在我们有临时扩展),如果移动你的对象比复制它们更便宜,那就是赢了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接