delete/default 关键字是否将相应的方法标记为用户定义的?
Does delete/default keywords mark the corresponding method as user defined?
我有以下代码:
#include <iostream>
class B{
public:
//this should all be generated by default but
//throwing them in here just to be sure
B() = default;
B(const B& b) = default;
B& operator=(const B& b) = default;
B(B&& b) = default;
B& operator=(B&& b) = default;
};
class A {
public:
A(B x) : x_(x) {}
A(const A& a) = delete;
A& operator=(const A& a) = delete;
//move operations should be generated by compiler?
private:
B x_;
};
int main() {
A a = A(B());
}
我希望它能够编译并使用它的移动构造函数创建一个 A
,但是它失败了,并显示以下消息:
error: use of deleted function ‘A::A(const A&)’
A a = A(B());
note: declared here
A(const A& a) = delete;
当然,添加移动操作并用默认关键字标记它们可以解决问题。我是否应该假设移动操作不是由编译器生成的,为什么呢? delete 关键字是否将方法标记为用户实现的,因此不会生成移动操作?为什么首选复制构造函数?我正在使用 gcc 进行编译。
如果您提供复制构造函数的实现/operator=
,默认情况下不再生成移动操作。如果你想要它们,你需要明确地告诉你。
根据 cppreference:
If no user-defined move constructors are provided for a class type
(struct, class, or union), and all of the following is true:
- there are no user-declared copy constructors;
- there are no user-declared copy assignment operators;
- there are no user-declared move assignment operators;
- there are no user-declared destructors;
then the compiler will declare a move constructor as a non-explicit
inline public member of its class with the signature T::T(T&&)
.
这是针对这种情况的有用图表:
说 = delete
你不只是说代码不存在,而是说如果代码试图调用它你应该得到一个编译错误。
更具体地说,delete
d 成员不会简单地从可用选项集中删除:它们会被搜索,如果它们匹配,则会出现编译错误。换句话说,= delete
适用于匹配时暗示使用问题的签名,而不适用于您不想匹配的签名。
例如 delete
d 成员 do indeed participate in overload resolution.
提供 = delete
与提供实现 完全相同,不同之处在于如果编译器最终生成将调用已删除函数的代码,那么您将进行编译错误。如果您提供了一个 delete
d 复制构造函数,您仍然提供了一个用户定义的复制构造函数。
您应该注意的另一个微妙之处是,自动生成移动 constructor/assignment 取决于 "user defined copy constructor" 的存在以及该定义的严格含义。逻辑上不明显的东西(至少对我来说不是)是匹配复制构造函数签名的模板不是复制构造函数。换句话说:
struct Foo {
template<typename T>
Foo(const T& other) { ... }
};
模板可以匹配Foo(const Foo&)
但是不被认为是用户定义的复制构造函数(不要寻找深层次的原因,这样是因为标准是这样说的)因此你仍然会得到隐式生成的移动构造函数。如果默认实现不正确,这将是严重问题的根源...
我有以下代码:
#include <iostream>
class B{
public:
//this should all be generated by default but
//throwing them in here just to be sure
B() = default;
B(const B& b) = default;
B& operator=(const B& b) = default;
B(B&& b) = default;
B& operator=(B&& b) = default;
};
class A {
public:
A(B x) : x_(x) {}
A(const A& a) = delete;
A& operator=(const A& a) = delete;
//move operations should be generated by compiler?
private:
B x_;
};
int main() {
A a = A(B());
}
我希望它能够编译并使用它的移动构造函数创建一个 A
,但是它失败了,并显示以下消息:
error: use of deleted function ‘A::A(const A&)’ A a = A(B()); note: declared here A(const A& a) = delete;
当然,添加移动操作并用默认关键字标记它们可以解决问题。我是否应该假设移动操作不是由编译器生成的,为什么呢? delete 关键字是否将方法标记为用户实现的,因此不会生成移动操作?为什么首选复制构造函数?我正在使用 gcc 进行编译。
如果您提供复制构造函数的实现/operator=
,默认情况下不再生成移动操作。如果你想要它们,你需要明确地告诉你。
根据 cppreference:
If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
- there are no user-declared copy constructors;
- there are no user-declared copy assignment operators;
- there are no user-declared move assignment operators;
- there are no user-declared destructors;
then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature
T::T(T&&)
.
这是针对这种情况的有用图表:
说 = delete
你不只是说代码不存在,而是说如果代码试图调用它你应该得到一个编译错误。
更具体地说,delete
d 成员不会简单地从可用选项集中删除:它们会被搜索,如果它们匹配,则会出现编译错误。换句话说,= delete
适用于匹配时暗示使用问题的签名,而不适用于您不想匹配的签名。
例如 delete
d 成员 do indeed participate in overload resolution.
提供 = delete
与提供实现 完全相同,不同之处在于如果编译器最终生成将调用已删除函数的代码,那么您将进行编译错误。如果您提供了一个 delete
d 复制构造函数,您仍然提供了一个用户定义的复制构造函数。
您应该注意的另一个微妙之处是,自动生成移动 constructor/assignment 取决于 "user defined copy constructor" 的存在以及该定义的严格含义。逻辑上不明显的东西(至少对我来说不是)是匹配复制构造函数签名的模板不是复制构造函数。换句话说:
struct Foo {
template<typename T>
Foo(const T& other) { ... }
};
模板可以匹配Foo(const Foo&)
但是不被认为是用户定义的复制构造函数(不要寻找深层次的原因,这样是因为标准是这样说的)因此你仍然会得到隐式生成的移动构造函数。如果默认实现不正确,这将是严重问题的根源...