考虑以下类.
struct with_copy { with_copy() = default; with_copy(with_copy const&) {} with_copy& operator=(with_copy const&) { return *this; } }; struct foo { with_copy c; std::unique_ptrp; };
是否with_copy
有一个拷贝构造函数?是.它是明确定义的.
是否with_copy
有一个移动构造函数?不可以.显式复制构造函数阻止生成它.
是否with_copy
有删除的移动构造函数?不.没有移动构造函数与删除构造函数不同.删除的移动构造函数会尝试将格式错误而不是退化移动到副本.
可with_copy
复制吗?是.其复制构造函数用于复制.
是with_copy
可移动的?是.它的复制构造函数用于移动.
......现在是棘手的.
是否foo
有一个拷贝构造函数?是.它有一个删除的,因为它的默认定义将由于调用unique_ptr
已删除的复制构造函数而格式不正确.
是否foo
有一个移动构造函数?GCC说是的,clang说没有.
是否foo
有删除的移动构造函数?海湾合作委员会和铿锵声说不.
可foo
复制吗?不会.它的复制构造函数被删除.
是foo
可移动的?GCC说是的,clang说没有.
(当考虑赋值而不是构造时,行为类似.)
据我所知,海湾合作委员会是正确的.foo
应该有一个移动构造函数,在每个成员上执行移动,在这种with_copy
情况下退化为副本.Clang的行为似乎很荒谬:我有一个有两个可移动成员的聚合体,但我的聚合物是一个不可移动的砖块.
谁是对的?
我不太确定你测试的foo
是什么,但它肯定都是可移动的,可移动的.不可否认,这并没有说明移动构造函数或移动赋值是否可访问,只是来自右值的构造或赋值有效.这两个铛(铛版本3.5(主干196718))和海湾合作委员会(GCC版本4.9.0 20131031(实验)(GCC))同意这样的评价.这是我尝试的完整来源:
#include <iostream> #include <type_traits> #include <memory> struct with_copy { with_copy() = default; with_copy(with_copy const&) {} with_copy& operator=(with_copy const&) { return *this; } }; struct foo { with_copy c; std::unique_ptr<int> p; }; int main() { std::cout << "move constructible: " << std::is_move_constructible<foo>::value << '\n'; std::cout << "move assignable: " << std::is_move_assignable<foo>::value << '\n'; foo f0; foo f1 = std::move(f0); f0 = std::move(f1); }
C++ 11,或者更确切地说是n3485,[class.copy]/9:
如果类的定义
X
没有显式声明一个移动构造函数,那么当且仅当一个类的定义被隐式声明为默认值时
X
没有用户声明的复制构造函数,
X
没有用户声明的复制赋值运算符,
X
没有用户声明的移动赋值运算符,
X
没有用户声明的析构函数,和移动构造函数不会被隐式定义为已删除.
和/ 11:
隐式声明的复制/移动构造函数是
inline public
其类的成员.X
如果X
具有以下内容,则将类的默认复制/移动构造函数定义为已删除(8.4.3):
[...]
对于复制构造函数,rvalue引用类型的非静态数据成员,或
对于移动构造函数,一个非静态数据成员或直接或虚拟基类,其类型没有移动构造函数,并且不是简单的可复制.
由于with_copy
是不平凡的拷贝,但foo
将不动器构造器(它会被定义为删除,因此它不会被隐式声明).
C++ 1y,或者更确切地说是2013年12月12日的github repo commit e31867c0; 合并DR1402:
/ 9:
如果类的定义
X
没有显式声明一个移动构造函数,那么当且仅当一个类的定义被隐式声明为默认值时
X
没有用户声明的复制构造函数,
X
没有用户声明的复制赋值运算符,
X
没有用户声明的移动赋值运算符,和
X
没有用户声明的析构函数.
和/ 11:
隐式声明的复制/移动构造函数是
inline public
其类的成员.X
如果X
具有以下内容,则将类的默认复制/移动构造函数定义为已删除(8.4.3):
[...]
对于复制构造函数,rvalue引用类型的非静态数据成员.
超载解析(13.3,13.4)忽略定义为已删除的默认移动构造函数.
这里foo
将有一个move-constructor.