C & C++

C & C++

Made by DeepSource

Move constructor is calling the copy constructor of base class CXX-P2011

Performance
Minor

To properly initialize class data members or base classes in a move constructor's initializer list, it's important to ensure that the object hierarchy is being move-initialized, rather than copy-initialized. An rvalue reference parameter is itself an lvalue and steps must be taken to preserve move semantics. See the examples below(1).

The lvalue can be turned into an xvalue(same as glvalue & rvalue) by passing it to std::move and it can be used to perform initialization.

But for trivially copyable objects we shouldn't use std::move, as it can negatively impact performance.

Bad practice

struct BaseClass {
    BaseClass() = default;
    BaseClass(const BaseClass& other);
    BaseClass(BaseClass&& other);
};

struct DerivedClass : BaseClass {
    DerivedClass() = default;
    DerivedClass(const DerivedClass& other) : BaseClass(other) {}
    // (1) In the below line, the rvalue reference parameter
    // is treated as lvalue when passed to BaseClass().
    // Hence the same call will result in invocation of
    // copy-constructor.
    DerivedClass(DerivedClass&& other) : BaseClass(other) {}
};

struct EnclosingClass {
    BaseClass B;
    // The member variable is copy-initialised rather then move initilised
    BaseClass(BaseClass& i) : B(i);
};

Recommended

struct BaseClass {
  BaseClass() = default;
  BaseClass(const BaseClass &other);
  BaseClass(BaseClass &&other);
};

struct DerivedClass : BaseClass {
  DerivedClass() = default;
  DerivedClass(const DerivedClass &other) : BaseClass(other) {}
  // (1) In the below line, the rvalue reference parameter is explicitly
  // converted to rvalue using std::move hence resulting in invocation of move
  // constructor.
  DerivedClass(DerivedClass &&other) : BaseClass(std::move(other)) {}
};

struct EnclosingClass {
  BaseClass B;
  // The member variable is copy-initialised rather then move initilised
  BaseClass(BaseClass& i) : B(i);
};