-1

I am trying to find out why in the following example, when I disable copy-semantics but preserve move-semantics, emplacing into std::vector works in one case but does not work in another (when inheritance is used):

#include <iostream>
#include <vector>

class NonCopyable
{
  public:
    NonCopyable(const NonCopyable &) = delete;
    NonCopyable(NonCopyable &&) noexcept = default;
    NonCopyable &operator=(const NonCopyable &) = delete;
    NonCopyable &operator=(NonCopyable &&) noexcept = default;

    NonCopyable() = default;
    virtual ~NonCopyable() = default;
};

// NOTE: here things works as long as I dont override the destructor. If I do, it stops.
class MyClass : public NonCopyable
{
  public:
    MyClass() = default;
    
    // NOTE: when commented out, all compiles fine, otherwise not
    ~MyClass() override {}
};

// NOTE: when all is put into a single class, everything compiles ok
class MyClass2
{
  public:
    MyClass2() = default;
    ~MyClass2() noexcept
    {
    }

    MyClass2(const MyClass2 &) = delete;
    MyClass2 &operator=(const MyClass2 &) = delete;
    MyClass2(MyClass2 &&) noexcept = default;
    MyClass2 &operator=(MyClass2 &&) noexcept = default;
};

int main()
{
    std::vector<MyClass> mc;
    MyClass a;
    mc.emplace_back(std::move(a));

    std::vector<MyClass2> mc2;
    MyClass2 a2;
    mc2.emplace_back(std::move(a2));
}

Example in online compiler: https://onlinegdb.com/UTWju9WkU

What do I miss ? Thanks!

Enmaniac
  • 19
  • 3
  • 4
    Writing a custom destructor removes the implicit move operations. It should remove the implicit copy operations too, but doesn't yet (which is deprecated). – HolyBlackCat Jul 30 '23 at 22:27
  • What does "does not work" mean? Please post a error message. I am pretty sure it has an answer similar to the first comment. – 273K Jul 30 '23 at 22:27
  • Added the link to online compiler since error message is quite long...templates... – Enmaniac Aug 03 '23 at 21:07

1 Answers1

0

The rule of 5 states that if you touch any of the 5 special member functions (copy ctor, copy assign, move ctor, move assign, dtor) you should touch them all. Not doing so is a bad plan. (The rule of 5 is also known as the rule of 3, from back before move operations existed).

In this case, when you implement ~MyClass() the compiler no longer synthesizes your move constructor. The move constructor is needed to emplace_back, because it is used if the vector is resized.

The standard has the exact rules, but you don't need to memorize them. You should just remember the rule of 5.

(The other rule to know here is the rule of 0 -- don't touch them unless you are writing a resource-management class. And if you are writing a resource-management class, it should just do resource management; use composition to store the resource in your business logic class. Then your business logic class follows the rule of 0, and gets its move/copy/destruction semantics from its members.)

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thing is that in both cases we define all in the same way. In the first case, we delete copy constructor and assignment operator, mark move ones, default constuctor and destructor as default via base class. In the second case ,we define all these exacty the same but without inheritance, directly in the class. First scenario generates compile error while the second does not. Thats what I dont understand. – Enmaniac Aug 03 '23 at 21:13
  • @Enmaniac You touch any of the 5 special member functions in a class, you should touch all 5. You violated this in one example, and not the other. What you did in the base class doesn't fix your rule of 5 violation: base classes are not the same as the class. What about the rule of 5 is unclear? Other than "I don't want to follow the rule of 5", which is fair but sort of your problem. – Yakk - Adam Nevraumont Aug 03 '23 at 21:28