为什么派生类移动时可以构build基类不是?
考虑下面的例子:
#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?! }
为什么编译器生成一个移动构造函数,尽pipe基类是不可移动的可构造的?
这是标准还是编译器错误? 是否有可能“完美地宣传”从基地到派生class的build设?
因为:
定义为删除的默认移动构造函数被重载parsing忽略。
([class.copy] / 11)
Bar
的移动构造函数被明确删除 ,所以Bar
不能被移动。 但是由于Bar
成员不能被移动的事实, Foo<Bar>
的移动构造函数被隐含地声明为默认的后隐式地被删除 。 因此可以使用它的拷贝构造函数移动Foo<Bar>
。
编辑:我也忘了提及一个重要的事实,例如using Base::Base
的inheritance构造函数声明不inheritance默认,复制或移动构造函数,所以这就是为什么Foo<Bar>
没有显式删除移动构造函数inheritance从Bar
。
1. std::is_move_constructible
的行为
这是std :: is_move_constructible的预期行为:
没有移动构造函数的types,但是接受
const T&
参数的复制构造const T&
满足std::is_move_constructible
。
这意味着与复制构造函数仍然有可能从右值引用T&&
构造T
而Foo<Bar>
有一个隐式声明的拷贝构造函数 。
2.隐式声明的Foo<Bar>
移动构造函数
为什么编译器生成移动构造函数,尽pipe基类是不可移动的可构造的?
事实上, Foo<Bar>
的移动构造函数被定义为已删除 ,但请注意,已删除的隐式声明的移动构造函数被重载parsing忽略。
T
类的隐式声明或默认移动构造函数被定义为在以下任一项中被删除为真:... T has direct or virtual base class that cannot be moved (has deleted, inaccessible, or ambiguous move constructors); ...
被删除的隐式声明的移动构造函数被重载parsing忽略(否则会阻止右值的复制初始化)。
3. Bar
和Foo<Bar>
之间的不同行为
请注意, Bar
的移动构造函数被声明为显式deleted
,并且Foo<Bar>
的移动构造函数被隐式声明并被定义为deleted
。 重点是删除的隐式声明的移动构造函数被重载parsing忽略 ,这使得构造函数Foo<Bar>
和它的拷贝构造函数一起移动。 但是显式删除的移动构造函数将参与重载parsing,意思是当试图移动构造函数Bar
删除的移动构造函数将被选中,然后该程序格式不正确。
这就是为什么Foo<Bar>
是可移动的,但Bar
不是。
标准对此有明确的说明。 $ 12.8 / 11复制和移动类对象[class.copy]
定义为删除的默认移动构造函数被重载parsing([over.match],[over.over])忽略。 [注意:删除的移动构造函数会影响可以使用复制构造函数的右值的初始化。 – 结束注意]