Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicit copy constructor and std::sort

When sorting a container of objects having an explicit copy ctor I get compiler errors (from g++ 4.8.2 and clang++ 3.4, both in -std=c++11 mode) that I don't understand. I've created a simple example to demonstrate the problem

class A {
public:
  explicit A(int i): m_i(i) {};
  explicit A(const A& other): m_i(other.m_i) {};
  int i() const {return m_i;};
private:
  int m_i;
};

bool is_less(const A& a, const A& b) {
  return a.i() < b.i();
}

int main(int, char*[]) {
  std::vector<A> objects;
  objects.push_back(A(3));
  objects.push_back(A(5));
  objects.push_back(A(-1));

  std::cout << is_less(objects[1], objects[2]);
  std::sort(objects.begin(), objects.end(), is_less);

  for (auto& a: objects) {
    std::cout << a.i() << " ";
  }
  std::cout << std::endl;
}

This fails with

error: 
  no matching constructor for initialization of '_ValueType' (aka 'A')

in clang++ and with

error: no matching function for call to ‘A::A(std::remove_reference<A&>::type)

in g++. The code compiles and works fine if the copy constructor is not explicit (but I want to enforce that only references to my objects can be used as parameters and return values). The code also compiles after removing the call to std::sort (so is_less(objects[1], objects[2]) is not a problem). Hence my question is what std::sort does when calling the comparing function that makes compiling this code fail and how to fix it.

After a lot of research, the only question that came close to my problem is In copy-initialization, is the call to the copy constructor explicit or implicit? which links to a bug in gcc. However, clang shows the same behavior, so I'd really like to understand what's going on.

like image 711
Gerald Senarclens de Grancy Avatar asked Aug 27 '14 14:08

Gerald Senarclens de Grancy


1 Answers

std::sort requires that the element type be MoveConstructible.

The requirements for MoveConstructible state that the expression T u = rv; must be valid. However, this expression performs copy initialization and requires that there is a non-explicit copy or move constructor.

In this case, the copy constructor is explicit, and declaring it means that there is no implicitly-declared move constructor. Therefore, the expression is invalid, and the class A is not MoveConstructible.

like image 56
interjay Avatar answered Oct 01 '22 03:10

interjay