1

Consider the following code. In order for this to compile, I HAVE to provide a body for the move constructor. Having "TestClass(TestClass&& other) = default" does not compile with an error that says "TestClass::TestClass(TestClass&&)' is implicitly deleted because the default definition would be ill-formed:"

Why is this? Why can't it figure out the default?

#include <iostream>
#include <optional>
#include <boost/noncopyable.hpp>

class TestClass : public boost::noncopyable {
public:
    TestClass() {
        std::cout << "Constructed...\n";
    }

    TestClass(TestClass&& other) = default;

    //TestClass(TestClass&& other)  {
    //    std::cout << "Move constructor...\n";
    //}

    TestClass& operator=(TestClass&& other)  {
        std::cout << "Move assignment...\n";
        return *this;
    }
};

TestClass doThing() {
    std::optional<TestClass> tc = std::make_optional<TestClass>(TestClass{});
    return std::move(*tc);
}

int main() {
    const TestClass t = doThing();
}

https://godbolt.org/z/3f5zTT6a9

rmsantos
  • 105
  • 6

1 Answers1

4

boost::noncopyable originates from before C++11. So maybe the name isn't making this obvious, but it doesn't only make the class non-copyable, it also makes it non-movable (with move semantics having been added only with C++11). If the base class can't be moved, then a default implementation of the move constructor is not possible.

Also it should be used by private inheritance, not public.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • they could have modernized it by making it moveable, but I suppose since `= delete;` is a thing modern code needs it less anyhow – 463035818_is_not_an_ai Dec 16 '22 at 16:22
  • 1
    @463035818_is_not_a_number It is supposed to be used to protect from unintended implicit copies in classes that can't follow the rule-of-zero otherwise. If copies weren't intended, then the implicit move behavior is also likely unintended. So that would have a good chance of breaking code. For the same reason declaring a copy operation in any way inhibits the declaration of implicit move operations without having to delete these explicitly. – user17732522 Dec 16 '22 at 16:24
  • not sure but I think howard got it wrong here https://stackoverflow.com/a/7841332/4117728, when he writes "When you add move-only types, I even see the documentation as misleading. The following two examples are not copyable, though they are movable: .." its basically OPs example – 463035818_is_not_an_ai Dec 16 '22 at 16:26
  • @463035818_is_not_a_number Yes, looks like a mistake to me. The post was written just in 2011, but I don't think there were any different rules for this. – user17732522 Dec 16 '22 at 16:41
  • not completely sure. In the source the only difference seems to be that prior to C++11 the members were private while now there are `=delete`. Should effectively be the same – 463035818_is_not_an_ai Dec 16 '22 at 16:43
  • well they are `private` when you compile as – 463035818_is_not_an_ai Dec 16 '22 at 16:45
  • @463035818_is_not_a_number If overload resolution would use a `private` constructor it should have the same effect as choosing a deleted one. I looked into the final C++11 draft and that rule was unchanged there. I did not check whether there had been changes prior to the final draft (which I think is from January 2012) since they are not nicely tracked in version control. – user17732522 Dec 16 '22 at 16:49