考虑以下类别。
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;
};
with_copy
是否有复制构造函数?是。它是明确定义的。with_copy
是否有移动构造函数?不可以。显式复制构造函数会阻止生成它。with_copy
是否有删除的移动构造函数?不。没有移动构造函数与删除构造函数不同。删除的移动构造函数会尝试将格式错误而不是退化移动到副本。with_copy
是否可以复制?是。它的拷贝构造函数用于拷贝。with_copy
可移动吗?是。它的复制构造函数用于移动。......现在是棘手的。
foo
是否有复制构造函数?是。它有一个删除的,因为它的默认定义因为调用unique_ptr
的已删除的复制构造函数而格式不正确。foo
是否有移动构造函数? GCC说是的,clang说没有。foo
是否有删除的移动构造函数? GCC和clang都说没有。foo
是否可以复制?不会。它的副本构造函数已删除。foo
可移动吗? GCC说是的,clang说没有。(当考虑赋值而不是构造时,行为类似。)
据我所知,海湾合作委员会是正确的。 foo
应该有一个移动构造函数,对每个成员执行移动,在with_copy
的情况下,它会退化为副本。 Clang的行为似乎很荒谬:我有一个有两个可移动成员的聚合体,但我的聚合物是一个不可移动的砖块。
谁是对的?
答案 0 :(得分:8)
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,或者更确切地说是github repo在2013-11-12提交e31867c0;合并DR1402:
/ 9:
如果类
X
的定义未明确声明移动 构造函数,只有当一个隐式声明为默认值 如果
X
没有用户声明的复制构造函数,X
没有用户声明的副本分配运算符X
没有用户声明的移动赋值运算符和X
没有用户声明的析构函数。
和/ 11:
隐式声明的复制/移动构造函数是
,则定义为已删除(8.4.3)inline public
同类的成员。类X
的默认复制/移动构造函数 如果X
有:
- [...]
- 表示复制构造函数,是rvalue引用类型的非静态数据成员。
被忽略的默认移动构造函数被忽略 重载决议(13.3,13.4)。
在这里,foo
将有一个移动构造函数。
答案 1 :(得分:7)
我不太确定你测试的是什么,但它foo
肯定都是可分配的并且可以移动构造。不可否认,这并没有说明移动构造函数或移动赋值是否可访问,只是来自右值的构造或赋值有效。 clang(clang版本3.5(主干196718))和gcc(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);
}