In a boost::noncopyable class, setting the move constructor "=default" does not compile?
Asked Answered
M

1

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

Merman answered 16/12, 2022 at 16:8 Comment(3)
But you yourself ensured that it has non-movable and non-copyable base sub-object. How should it figure out what is impossible to do?Vociferant
fwiw boost.org/doc/libs/1_64_0/boost/core/noncopyable.hppAggappe
i.stack.imgur.com/142BI.jpg - note that deletion of function counts as its declaration in this table.Krakow
P
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.

Parallelogram answered 16/12, 2022 at 16:21 Comment(7)
they could have modernized it by making it moveable, but I suppose since = delete; is a thing modern code needs it less anyhowAggappe
@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.Parallelogram
not sure but I think howard got it wrong here https://mcmap.net/q/129953/-what-are-the-advantages-of-boost-noncopyable, 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 exampleAggappe
@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.Parallelogram
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 sameAggappe
well they are private when you compile as <C++11 and then there is no moving anyhow. Though maybe there was some version in between, but even then I'd expect non moveableAggappe
@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.Parallelogram

© 2022 - 2025 — McMap. All rights reserved.