C++ 为什么基类不是';T
考虑以下示例:C++ 为什么基类不是';T,c++,c++11,language-lawyer,move-semantics,move-constructor,C++,C++11,Language Lawyer,Move Semantics,Move Constructor,考虑以下示例: #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
#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?!
}
#包括
#包括
#包括
模板结构Foo:public Base{
使用Base::Base;
};
结构条{
Bar(常数Bar&){}
条形图(条形图&&)=删除;
};
int main(){
std::cout1.std::的行为是可构造的
这是以下人员的预期行为:
没有移动构造函数的类型,但具有接受const T&
参数的复制构造函数,满足std::is\u move\u constructible
这意味着使用复制构造函数,仍然可以从右值引用T&
构造T
。并且Foo
具有
2.隐式声明的Foo的move构造函数
为什么编译器会在基类不可移动构造的情况下生成移动构造函数
事实上,Foo
的移动构造函数定义为,但请注意,重载解析会忽略已删除的隐式声明的移动构造函数
类T
的隐式声明或默认移动构造函数为
在以下任何一项中定义为“已删除”是正确的:
...
T has direct or virtual base class that cannot be moved (has deleted, inaccessible, or ambiguous move constructors);
...
重载解析将忽略已删除的隐式声明的移动构造函数(否则将阻止从右值进行复制初始化)
3.Bar
和Foo
之间的不同行为
请注意,Bar
的移动构造函数被显式声明为deleted
,而Foo
的移动构造函数被隐式声明并定义为deleted
。关键是重载解析将忽略已删除的隐式声明的移动构造函数使用其复制构造函数移动构造函数Foo
。但显式删除的移动构造函数将参与重载解析,这意味着在尝试移动构造函数Bar
时,将选择删除的移动构造函数,则程序格式不正确
这就是为什么Foo
是可移动构造的,而Bar
不是
标准对此有明确的规定
重载解析([over.match]、[over.over])会忽略定义为已删除的默认移动构造函数。[注意:删除的移动构造函数会干扰右值的初始化,右值可以使用复制构造函数。-结束注意]
因为:
重载解析将忽略定义为已删除的默认移动构造函数
([class.copy]/11)
Bar
的移动构造函数被显式删除,因此无法移动Bar
。但是Foo
的移动构造函数在隐式声明为默认值后被隐式删除,因为无法移动Bar
成员。因此可以使用其复制构造函数移动Foo
编辑:我还忘了提到一个重要的事实,即继承构造函数声明(如使用Base::Base
)不会继承默认、复制或移动构造函数,因此Foo
没有从Bar
继承的显式删除的移动构造函数,除非坚持编写可复制但不可移动的类,没有理由这样做。这个问题背后的想法是创建非侵入性包装类的可能性,它存在与其基础完全相同的外部行为。另外,选择接受哪个答案确实很困难(我不能同时接受这两个答案,对吧?):)@Dmitry:不,你不能接受超过一个,但是你应该在任何可能帮助你理解问题的尝试上投票。顺便说一句,暴露非直觉行为的好问题!@Brian:class famouspaintingthelouvre
?@einpoklum你不会删除这种类的移动构造函数,你只会让从n rvalue使用复制构造函数。嗯,我不明白这一点。原因似乎是“但是Foo的move构造函数在隐式声明为默认值后被隐式删除,因为Bar成员无法移动。因此可以使用其复制构造函数移动Foo。”正如您所描述的。那么,为什么它不这么认为呢“Bar的移动构造函数被显式删除,因此可以使用其复制构造函数移动Bar”?我认为“因此Foo
可以使用其复制构造函数移动。”的措词非常混乱。也许最好是以一种不暗示对象正在从中移动的方式重写,只是(示例)调用Foo(std::move(other))
是合法的。或者别的什么。@AndreasPapadopoulos在我看来,最好说“Foo
可以用它的复制构造函数进行移动构造”。@Andreas对于像我这样的凡人来说,“移动构造”和“移动到”似乎没有区别“。如果对于一个基,谓词a
不成立,那么对于一个派生类,它需要对a
声明可能性的base
对象执行操作,谓词突然产生true
,这似乎非常令人困惑。如果这属于“缺点”,那么对我来说似乎是合乎逻辑的。”在这些谓词中,它们可能会错过在“非即时上下文”中发生的错误,如边实例化等。但如果有概念上的原因,我还不明白。@Johanneschaub litb:两者之间的关键区别在于Base
显式删除移动向量(->移动时的编译时错误)而A
由于宋元耀未完全命名的子句中指定的内容而隐式删除它。由于隐式删除的move-ctors在尝试移动A
的实例时不被考虑,并且因为A::A(const&)