为什么 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&&
构造 T
。 Foo<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。 Bar
和 Foo<Bar>
之间的不同行为
请注意,Bar
的移动构造函数显式声明为 deleted
,Foo<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
.
继承的明确删除的移动构造函数
考虑以下示例:
#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, satisfystd::is_move_constructible
.
这意味着使用复制构造函数仍然可以从右值引用 T&&
构造 T
。 Foo<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。 Bar
和 Foo<Bar>
请注意,Bar
的移动构造函数显式声明为 deleted
,Foo<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
.