Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect whether a class has a move constructor?

I'd like to detect (and use the result in std::enable_if) whether a C++ class has a move constructor defined.

The following program prints MOVE, so using std::is_move_constructible is not the way to do it:

#include <stdio.h>
#include <type_traits>
class C {
 public:
  C() { puts("C()"); }
  C(int) { puts("C(int)"); }
  ~C() { puts("~C()"); }
  C(const C&) { puts("C(const C&)"); }
  // C(C&&) { puts("C(C&&)"); }
  C& operator=(const C&) { puts("C="); return *this; }
};
int main(int argc, char** argv) {
  (void)argc; (void)argv;
  if (std::is_move_constructible<C>::value) puts("MOVE");
  return 0;
}

I need a program which prints MOVE only if I uncomment the line containing the &&.

like image 383
pts Avatar asked Feb 23 '17 18:02

pts


People also ask

What is a move constructor?

A move constructor allows the resources owned by an rvalue object to be moved into an lvalue without creating its copy. An rvalue is an expression that does not have any memory address, and an lvalue is an expression with a memory address.

Are move constructor automatically generated?

If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.

Does STD move move constructor?

std::move is actually just a request to move and if the type of the object has not a move constructor/assign-operator defined or generated the move operation will fall back to a copy.

How do I turn off move constructor?

To correct this, remove the move constructor completely. In the case of the class, once a copy constructor is present (user defined), the move is implicitly not generated anyway (move constructor and move assignment operator).


1 Answers

Short answer: it's impossible.

Some more details, based on the comment by @TobySpeight:

If the class doesn't contain C(C&&) = delete;, then it's impossible to detect whether it contains C(C&&) { ... } or not: std::is_move_constructible<C>::value will be true in either case, and there is no other way to detect it.

The presence of C(C&&) = delete; can be detected: std::is_move_constructible<C>::value is false iff C(C&&) = delete; is present.

More explanation in this answer to "Understanding std::is_move_constructible".

To avoid slow copying in std::vector::push_back, detecting a user-defined move constructor is not necessary. This is the alternative, based on @NirFriedman's comment:

  • All classes have a copy constructor.
  • Old (C++98) classes have a member swap and 0-argument constructor (can be implicitly generated by the compiler), and they don't have a user-defined move constructor.
  • New (C++11) classes don't have a member swap (but they can have a namespace-level swap or a friend swap), and they have a user-defined move constructor.
  • Small classes (for which we don't care about the speed of the copy constructor, it's always fast enough) may have a user-defined move constructor. They also may have a member swap (but they shouldn't, for speed). If they have a member swap, they also have a 0-argument constructor (can be implicitly generated by the compiler).
  • SFINAE is used to to detect the member swap.
    • If the member swap is present, resize + back + swap is used to add a new element to the vector.
    • Otherwise, push_back is used.

For old classes, this will use the member swap (fast). For new classes, it will use the move constructor (fast, also a little bit faster). For small classes, it will use the copy constructor (assumed to be fast enough for small classes) or the move constructor (if available).

Here is my final solution for a fast std::vector::push_back with member swap detection: https://github.com/pts/fast_vector_append/blob/master/fast_vector_append.h

like image 173
pts Avatar answered Oct 08 '22 00:10

pts