2

I'm trying to sort a vector of class that implements move assignment operator. This code works fine in Microsoft and Intel C++. In GCC 4.8.1, the copy constructor is deleted and seems causing problem.

c:\mingw\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_algo.h:2164:11: error: use of deleted function 'constexpr MoveOnly::MoveOnly(const MoveOnly&)'
__val = _GLIBCXX_MOVE(*__i);
        ^
test.cpp:6:11: note: 'constexpr MoveOnly::MoveOnly(const MoveOnly&)' is implicitly declared as deleted because 'MoveOnly' declares a move constructor or move assignment operator

And with help from Matthieu M., this page explained why the copy constructor is deleted.

#include <vector>
#include <algorithm>
#include <iostream>
#include <type_traits>

class MoveOnly {

public:
    int data;
    MoveOnly& operator = (const MoveOnly && rhs) {
        data = rhs.data;
        return *this;
    }
    MoveOnly& operator = (const MoveOnly & rhs) {
        data = rhs.data;
        return *this;
    }
    bool operator < (const MoveOnly& j) const {
        return data<j.data;
    }
};

int main() {

    std::cout<<"Is move_assignable:"<<std::is_move_assignable<MoveOnly>::value<<std::endl;
    std::cout<<"Is copy_assignable:"<<std::is_copy_assignable<MoveOnly>::value<<std::endl;
    std::vector<MoveOnly> vMoveOnly;
    //std::sort(vMoveOnly.begin(), vMoveOnly.end());
    return 0;
}
hivert
  • 10,579
  • 3
  • 31
  • 56

2 Answers2

5

Declaring a move constructor or move assignment operator deletes the default copy/move constructors (IMO due to encourage us to obey rule of five!), on the other hand std::sort needs one of move-construct or copy-construct having this code:

    template<typename _RandomAccessIterator>
    void
    __insertion_sort(_RandomAccessIterator __first,
             _RandomAccessIterator __last)
    {
      if (__first == __last)
    return;

    for (_RandomAccessIterator __i = __first + 1; __i != __last; ++__i)
    {
      if (*__i < *__first)
        {
          typename iterator_traits<_RandomAccessIterator>::value_type
        __val = _GLIBCXX_MOVE(*__i);
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

To solve the issue:

You need a class which is move-constructable and you can test it by is_move_constructible, std::sort needs first one. Put a move-constructor to make it move-constructable.

You need at least one of move-assignment or copy-assignment for assignment, and at least one of copy-constructor or move-constructor for construction.

While you class name is MoveOnly, I think it's reasonable to pick move-assignment and move-constructor. So, this code is enough to compile:

class MoveOnly {

    ...

    MoveOnly(MoveOnly &&m) : data(m.data) {}

    MoveOnly& operator = (const MoveOnly && rhs) { ... }
};
masoud
  • 55,379
  • 16
  • 141
  • 208
  • 1
    @user1662078: it is correct to delete it, the Standard mandates it, see http://stackoverflow.com/q/18895784/147192 – Matthieu M. Sep 27 '13 at 07:40
  • @MatthieuM. Thanks for the reference page. Although it's kind of strange with a move assignment operator I lost copy constructor.. – user1662078 Sep 27 '13 at 07:49
  • 1
    @user1662078: the basic answer is that as soon as your start meddling with copy construction, copy assignment or destruction you are doing something special and most likely the default generated methods would be **wrong**. In C++03 many people got bitten, so this defect was addressed in C++11 and to sweeten the deal you can always write `X(X const&) = default;` if you know the default is good *in your case*. But at least it's a conscious decision, and not an oversight. – Matthieu M. Sep 27 '13 at 08:03
-3

I have add a few things to your code and this works fine :

#include <vector>
#include <algorithm>
#include <iostream>
#include <type_traits>

class MoveOnly {

public:
    int data;
        MoveOnly (int i): 
        data (i)
        {}

    MoveOnly (MoveOnly && rhs): 
        data (std::move(rhs.data))
        {}
    MoveOnly (const MoveOnly & rhs)=delete;

    MoveOnly& operator = (MoveOnly && rhs) {
        data = std::move(rhs.data);
        return *this;
    }
    MoveOnly& operator = (const MoveOnly & rhs) {
        data = rhs.data;
        return *this;
    }
    bool operator < (const MoveOnly& j) const {
        return data<j.data;
    }
};

int main() {

    std::cout<<"Is move_assignable:"<<std::is_move_assignable<MoveOnly>::value<<std::endl;
    std::cout<<"Is copy_assignable:"<<std::is_copy_assignable<MoveOnly>::value<<std::endl;
    std::vector<MoveOnly> vMoveOnly;
    vMoveOnly.push_back(MoveOnly(10));
    vMoveOnly.push_back(MoveOnly(666));
    vMoveOnly.push_back(MoveOnly(-100));
    std::sort(vMoveOnly.begin(), vMoveOnly.end());
    for(std::size_t i=0;i<vMoveOnly.size();++i){std::cout<<vMoveOnly[i].data<<std::endl;}
    return 0;
}

PS : using a const r-value reference is kind of nonsense because the main purpose of rvalue references is to to move objects instead of copying them. And moving the state of an object implies modification. So no const

Davidbrcz
  • 2,335
  • 18
  • 27