Opened 6 years ago

Closed 6 years ago

#12194 closed Bugs (fixed)

Copy assignment on moveable and copyable classes uses wrong type

Reported by: a.grund@… Owned by: Ion Gaztañaga
Milestone: Boost 1.61.0 Component: move
Version: Boost 1.59.0 Severity: Regression
Keywords: Cc:

Description

A patch introduced a breaking change to classes that are copyable and movable: https://github.com/boostorg/move/commit/4f9c2b62fbdcf5995ecf50a2ecf2494048a6696d#diff-6a11d48d06dd33c1193ffb3d794787fbR252

The macro was changed so the TYPE& operator=(TYPE &t) calls the assignement operator with a type const TYPE& instead of const ::boost::rv<TYPE> &

This change is not described and seems to be a mistake that was not caught in the review process. As the macro BOOST_COPY_ASSIGN_REF still expands to the original rv-type a wrong operator might get called. A minimum example:

#include <boost/move/move.hpp>
#include <cassert>

class Foo{
    BOOST_COPYABLE_AND_MOVABLE(Foo)
public:
    int i;
    explicit Foo(int val): i(val){}
    Foo(BOOST_RV_REF(Foo) obj): i(obj.i) {}
    Foo& operator=(BOOST_RV_REF(Foo) rhs){ i = rhs.i; return *this; }
    Foo& operator=(BOOST_COPY_ASSIGN_REF(Foo) rhs){ i = rhs.i; return *this; }
    template<class OTHER>
    Foo& operator=(const OTHER& rhs){ i = rhs.j; return *this; }
};

struct Bar{
    int j;
    explicit Bar(int val): j(val){}
};

int main(){
    Foo foo1(1);
    Foo foo2(2);
    Bar bar(3);
    assert(foo1.i == 1);
    assert(foo2.i == 2);
    assert(bar.j == 3);
    foo2 = foo1;
    assert(foo1.i == 1);
    assert(foo2.i == 1);
    foo1 = bar;
    assert(foo1.i == 3);
    return 0;
}

This compiles and works fine in boost <=1.58 but fails in >=1.59 as the template version is called.

Change History (6)

comment:1 by a.grund@…, 6 years ago

I found during creation of a patch (https://github.com/boostorg/move/pull/9) that BOOST_COPY_ASSIGN_REF causes more problems. The class assignment fails also, when assigning const instances as that would also use the template function In C++11 it works perfectly though

comment:2 by Ion Gaztañaga, 6 years ago

Resolution: fixed
Status: newclosed

comment:4 by Ion Gaztañaga, 6 years ago

Resolution: fixed
Status: closedreopened

The patch breaks several Boost libraries (Container and Intrusive) so it's reverted:

https://github.com/boostorg/move/commit/1194a39ab3195a17c849e1d11f4305ff6727df8b

The alternative is to document this limitation with templated assignments, similarly to Issue #12307.

comment:5 by a.grund@…, 6 years ago

This is strange as it returns a 'const rv<...>&' which should be ok and is also documented this way.

If the other libraries can't be fixed, please document this back to 1.59 as that change was silently introduced (no release note) and changes the observed and documented behaviour:

"const rvalue and lvalues, bind to const ::boost::rv< TYPE >&"

But now it seems it is like: "const rvalue and lvalues, bind to const ::boost::rv< TYPE >& OR const TYPE&" or a mix of both depending on some unknown conditions.

This makes it very hard to use this in non-trivial/templated contexts

comment:6 by Ion Gaztañaga, 6 years ago

Resolution: fixed
Status: reopenedclosed

Documented limitation and possible workaround in commit:

https://github.com/boostorg/move/commit/cfd6be4ab46223917cb79e7dd856f582df587d7d

Note: See TracTickets for help on using tickets.