为什么 derived class move 可构造而 base class 不可构造?

Why is derived class move constructible when base class isn't?

考虑以下示例:

#include <iostream>
#include <string>
#include <utility>

template <typename Base> struct Foo : public Base {
    using Base::Base;
};

struct Bar {
    Bar(const Bar&) { }
    Bar(Bar&&) = delete;
};

int main() {
    std::cout << std::is_move_constructible<Bar>::value << std::endl; // NO
    std::cout << std::is_move_constructible<Foo<Bar>>::value << std::endl; // YES. Why?!
}

尽管基 class 是不可移动构造的,但为什么编译器会生成一个移动构造函数?

这是标准还是编译器错误?是否可以 "perfectly propagate" 将构造从基础移动到派生 class?

1. std::is_move_constructible

的行为

这是 std::is_move_constructible 的预期行为:

Types without a move constructor, but with a copy constructor that accepts const T& arguments, satisfy std::is_move_constructible.

这意味着使用复制构造函数仍然可以从右值引用 T&& 构造 TFoo<Bar> 有一个 Implicitly-declared copy constructor.

2。 Foo<Bar>

的隐式声明的移动构造函数

Why does compiler generates move constructor despite base class being non-move-constructible?

事实上,Foo<Bar>的移动构造函数定义为deleted,但请注意,删除的隐式声明的移动构造函数将被重载决议忽略。

The implicitly-declared or defaulted move constructor for class T is defined as deleted in any of the following is true:

...
T has direct or virtual base class that cannot be moved (has deleted, inaccessible, or ambiguous move constructors); 
...

The deleted implicitly-declared move constructor is ignored by overload resolution (otherwise it would prevent copy-initialization from rvalue).

3。 BarFoo<Bar>

之间的不同行为

请注意,Bar 的移动构造函数显式声明为 deletedFoo<Bar> 的移动构造函数隐式声明并定义为 deleted。重点是 已删除的隐式声明的移动构造函数被重载决议 忽略,这使得移动构造 Foo<Bar> 及其复制构造函数成为可能。但是显式删除的移动构造函数将参与重载决议,这意味着当尝试移动构造函数时Bar将选择删除的移动构造函数,然后程序是错误的。

这就是为什么 Foo<Bar> 可移动而 Bar 不是。

标准对此有明确的说明。 $12.8/11 复制和移动 class 个对象 [class.copy]

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

因为:

A defaulted move constructor that is defined as deleted is ignored by overload resolution.

([class.copy]/11)

Bar 的移动构造函数被显式删除,因此无法移动Bar。但是 Foo<Bar> 的移动构造函数在隐式声明为默认后被 隐式删除 ,因为 Bar 成员无法移动。因此 Foo<Bar> 可以使用其复制构造函数移动。

编辑:我也忘了提到一个重要的事实,即继承构造函数声明(例如 using Base::Base 不继承默认、复制或移动构造函数,所以这就是为什么 Foo<Bar> 没有从 Bar.

继承的明确删除的移动构造函数