Why class derived from non-movable class is itself move constructible? [duplicate]
Asked Answered
P

1

6

According to cppreference, deriving from a non-movable class should make a derived class non-movable, too. Then why does std::is_move_constructible_v return true for the derived class?

class NonMovable{
    public:
        NonMovable(const NonMovable&) = default;
        NonMovable(NonMovable&&) = delete;
        
        NonMovable& operator = (const NonMovable&) = default;
        NonMovable& operator = (NonMovable&&) = delete;
        
        NonMovable() = default;
};

class Derived : public NonMovable{};

int main(){
    std::cout << std::is_move_constructible_v<NonMovable> << "\n"; // 0
    std::cout << std::is_move_constructible_v<Derived> << "\n"; // 1
}
Pinnatipartite answered 7/6, 2021 at 23:46 Comment(7)
Types without a move constructor, but with a copy constructor that accepts const T& arguments, satisfy std::is_move_constructible.Phocine
That doesn't explain why is_move_constructible_v<NonMovable> is false.Mcmath
@S.M. In that case std::cout << std::is_move_constructible_v<NonMovable> << "\n"; would be 1 but it shows 0.Redouble
The question is about the second case. Also AFAIK deleted constructor does not involve class to be without constructor.Phocine
In the case of NonMovable, the move constructor is explicitly deleted. An explicitly deleted function still participates in overload resolution (but if it ends up winning, you get a compiler error). But in Derived, the move constructor is defaulted to deleted, so it simply doesn't participate at all. (and therefore cannot win a game that it doesn't play). "A defaulted move special member function that is defined as deleted is excluded from the set of candidate functions in all contexts.Hydrocele
When you explicitly delete the move members, good things never happen, and sometimes bad things do. A good style guideline is never delete the move members. A type with a deleted move and non-deleted copy is not a well-behaved type. For example when you derive from it, you get unexpected behavior.Delacroix
@RaymondChen: And this is why you shouldn’t explicitly delete move constructors: it does more than anyone wants (because the deletion model just doesn’t work well for that particular function, hence the magic), and you can already achieve what you want via the copy constructor.Kawasaki
B
3

The key clause appears to be the following:

Copy/move constructors [class.copy.ctor]

...

A defaulted move constructor that is defined as deleted is ignored by overload resolution. [ Note: A deleted move constructor would otherwise interfere with initialization from an rvalue which can use the copy constructor instead. — end note ]

Emphasis mine. The deleted move constructor is simply excluded from overload resolution, and the derived class's constructor ends up seleting the base class's copy constructor instead of the move constructor.

Boccioni answered 8/6, 2021 at 0:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.