Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it reasonable to overload operators by ref-qualifier to prevent temporaries?

It just occurred to me that operator+ & co can operate on this for rvalues; i.e. given a class C, it's possible to do this:

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 );
    }
};

This would prevent copies by simply modifying temporary values in-place.

Would this perform as I expect? Is it a reasonable optimization or would the compiler create equally fast code?

like image 357
Mr. Wonko Avatar asked Sep 03 '15 17:09

Mr. Wonko


1 Answers

Let's say we just wrap std::string and do a simplified version of 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(); }
};

With that, this works fine:

for (char c : C{"hello"}) { .. }

the range-for expression will extend the lifetime of the temporary, so we're ok. However, consider this:

for (char c : C{"hello"} + C{"goodbye"}) { .. }

We effectively have:

auto&& __range = C{"hello"}.operator+(C{"goodbye"});

Here, we're not binding a temporary to a reference. We're binding a reference. The object doesn't get its lifetime extended because... it's not an object. So we have a dangling reference, and undefined behavior. This would be very surprising to users who would expect this to work:

for (char c : std::string{"hello"} + std::string{"goodbye"}) { .. }

You'd have to return a value:

C operator+(const C& rhs) && {
    val += rhs.val;
    return std::move(*this);
}

That solves this issue (as now we have temporary extension), and if moving your objects is cheaper than copying them, this is a win.

like image 96
Barry Avatar answered Nov 19 '22 09:11

Barry