当派生 class 在基 class 中被删除时,它是否会拥有隐式复制构造函数或赋值运算符?
Would a derived class ever have an implicit copy constructor or assignment operator when it's deleted in the base class?
Qt defines Q_DISABLE_COPY
如下:
#define Q_DISABLE_COPY(Class) \
Class(const Class &) = delete;\
Class &operator=(const Class &) = delete;
Q_DISABLE_COPY
是 used in the QObject
class, but the documentation for it 说它也应该在它的所有子class 中使用:
when you create your own subclass of QObject (director or indirect), you should not give it a copy constructor or an assignment operator. However, it may not enough to simply omit them from your class, because, if you mistakenly write some code that requires a copy constructor or an assignment operator (it's easy to do), your compiler will thoughtfully create it for you. You must do more.
但是考虑这个程序:
struct Base {
Base() = default;
private:
Base(const Base &) = delete;
Base &operator=(const Base &) = delete;
};
struct Derived : Base {};
int main() {
Derived d1;
Derived d2(d1); // error: call to implicitly-deleted copy constructor of 'Derived'
Derived d3;
d3 = d1; // error: object of type 'Derived' cannot be assigned because its copy assignment operator is implicitly deleted
}
尝试编译该程序时出现的错误似乎表明编译器不会在派生的 classes 中创建复制构造函数或赋值运算符,而它们在基 classes 中被删除。 Qt 的文档在这方面是错误的,还是在创建它们时存在一些极端情况?
相关但不重复:Repeating Q_DISABLE_COPY in QObject derived classes。它给出了为什么在 class 中使用 Q_DISABLE_COPY
可能有用的原因,即使它无论如何都不可复制,但不能确认没有它它实际上永远不会被复制。
由于基 class 复制构造函数被删除,派生 class 无法知道如何复制基 class 对象。这将禁用编译器提供的任何隐式复制构造函数。
来自 cppreference:
The implicitly-declared or defaulted copy constructor for class T is
defined as deleted if any of the following conditions are true:
T has direct
or virtual base class that cannot be copied (has deleted,
inaccessible, or ambiguous copy constructors)
T has direct or virtual
base class with a deleted or inaccessible destructor;
当用户从删除默认复制构造函数的 class 继承时,继承 Q_DISABLE_COPY
会很有用,但会提供默认实现来覆盖它。
struct Base {
Base() = default;
private:
Base(const Base &) = delete;
Base &operator=(const Base &) = delete;
};
struct Derived : Base {
Derived() = default;
Derived(const Derived&) : Derived() {}
Derived &operator=(const Derived&) {
return *this;
}
};
struct MoreDerived : Derived {};
int main() {
Derived d1;
Derived d2(d1); // Works fine!
Derived d3;
d3 = d1; // Works fine!
MoreDerived md1;
MoreDerived md2(md1); // Works fine!
MoreDerived md3;
md3 = md1; // Works fine!!
}
编辑:正如@SR_ 正确指出的那样,在 Derived
的上述实现中,Base
没有被复制构造。我只是想说明一个事实,即在继承层次结构中修改另一个 class 时很容易引入无意的复制构造函数。
在提交 a2b38f6
, QT_DISABLE_COPY
was instead defined like this (credit to Swift - Friday Pie 之前在评论中指出这一点):
#define Q_DISABLE_COPY(Class) \
Class(const Class &) Q_DECL_EQ_DELETE;\
Class &operator=(const Class &) Q_DECL_EQ_DELETE;
和Q_DECL_EQ_DELETE
like this:
#ifdef Q_COMPILER_DELETE_MEMBERS
# define Q_DECL_EQ_DELETE = delete
#else
# define Q_DECL_EQ_DELETE
#endif
Q_COMPILER_DELETE_MEMBERS
如果 C++11 支持(或至少有足够新的草案支持 = delete
)可用,则得到定义。
因此,如果您当时针对 C++03 编译器编译 Qt,它会编译如下内容:
struct Base {
Base() {};
private:
Base(const Base &);
Base &operator=(const Base &);
};
struct Derived : Base {};
int main() {
Derived d1;
Derived d2(d1);
Derived d3;
d3 = d1;
}
用 g++ -std=c++03
编译会出现这些错误:
<source>: In copy constructor 'Derived::Derived(const Derived&)':
<source>:9:8: error: 'Base::Base(const Base&)' is private within this context
9 | struct Derived : Base {};
| ^~~~~~~
<source>:5:5: note: declared private here
5 | Base(const Base &);
| ^~~~
<source>: In function 'int main()':
<source>:13:18: note: synthesized method 'Derived::Derived(const Derived&)' first required here
13 | Derived d2(d1);
| ^
<source>: In member function 'Derived& Derived::operator=(const Derived&)':
<source>:9:8: error: 'Base& Base::operator=(const Base&)' is private within this context
9 | struct Derived : Base {};
| ^~~~~~~
<source>:6:11: note: declared private here
6 | Base &operator=(const Base &);
| ^~~~~~~~
<source>: In function 'int main()':
<source>:15:10: note: synthesized method 'Derived& Derived::operator=(const Derived&)' first required here
15 | d3 = d1;
| ^~
那时,“您的编译器会为您精心创建它”在技术上是正确的,但实际上并非如此,因为创建它的编译器会导致编译失败,只是出现不同的(并且可能不太清楚)错误。我现在确信,现在 = delete
被无条件使用,这根本不再是真的,所以我打算请 Qt 的维护者 remove/reword 他们文档的那一部分。
Qt defines Q_DISABLE_COPY
如下:
#define Q_DISABLE_COPY(Class) \
Class(const Class &) = delete;\
Class &operator=(const Class &) = delete;
Q_DISABLE_COPY
是 used in the QObject
class, but the documentation for it 说它也应该在它的所有子class 中使用:
when you create your own subclass of QObject (director or indirect), you should not give it a copy constructor or an assignment operator. However, it may not enough to simply omit them from your class, because, if you mistakenly write some code that requires a copy constructor or an assignment operator (it's easy to do), your compiler will thoughtfully create it for you. You must do more.
但是考虑这个程序:
struct Base {
Base() = default;
private:
Base(const Base &) = delete;
Base &operator=(const Base &) = delete;
};
struct Derived : Base {};
int main() {
Derived d1;
Derived d2(d1); // error: call to implicitly-deleted copy constructor of 'Derived'
Derived d3;
d3 = d1; // error: object of type 'Derived' cannot be assigned because its copy assignment operator is implicitly deleted
}
尝试编译该程序时出现的错误似乎表明编译器不会在派生的 classes 中创建复制构造函数或赋值运算符,而它们在基 classes 中被删除。 Qt 的文档在这方面是错误的,还是在创建它们时存在一些极端情况?
相关但不重复:Repeating Q_DISABLE_COPY in QObject derived classes。它给出了为什么在 class 中使用 Q_DISABLE_COPY
可能有用的原因,即使它无论如何都不可复制,但不能确认没有它它实际上永远不会被复制。
由于基 class 复制构造函数被删除,派生 class 无法知道如何复制基 class 对象。这将禁用编译器提供的任何隐式复制构造函数。
来自 cppreference:
The implicitly-declared or defaulted copy constructor for class T is defined as deleted if any of the following conditions are true:
T has direct or virtual base class that cannot be copied (has deleted, inaccessible, or ambiguous copy constructors)
T has direct or virtual base class with a deleted or inaccessible destructor;
当用户从删除默认复制构造函数的 class 继承时,继承 Q_DISABLE_COPY
会很有用,但会提供默认实现来覆盖它。
struct Base {
Base() = default;
private:
Base(const Base &) = delete;
Base &operator=(const Base &) = delete;
};
struct Derived : Base {
Derived() = default;
Derived(const Derived&) : Derived() {}
Derived &operator=(const Derived&) {
return *this;
}
};
struct MoreDerived : Derived {};
int main() {
Derived d1;
Derived d2(d1); // Works fine!
Derived d3;
d3 = d1; // Works fine!
MoreDerived md1;
MoreDerived md2(md1); // Works fine!
MoreDerived md3;
md3 = md1; // Works fine!!
}
编辑:正如@SR_ 正确指出的那样,在 Derived
的上述实现中,Base
没有被复制构造。我只是想说明一个事实,即在继承层次结构中修改另一个 class 时很容易引入无意的复制构造函数。
在提交 a2b38f6
, QT_DISABLE_COPY
was instead defined like this (credit to Swift - Friday Pie 之前在评论中指出这一点):
#define Q_DISABLE_COPY(Class) \
Class(const Class &) Q_DECL_EQ_DELETE;\
Class &operator=(const Class &) Q_DECL_EQ_DELETE;
和Q_DECL_EQ_DELETE
like this:
#ifdef Q_COMPILER_DELETE_MEMBERS
# define Q_DECL_EQ_DELETE = delete
#else
# define Q_DECL_EQ_DELETE
#endif
Q_COMPILER_DELETE_MEMBERS
如果 C++11 支持(或至少有足够新的草案支持 = delete
)可用,则得到定义。
因此,如果您当时针对 C++03 编译器编译 Qt,它会编译如下内容:
struct Base {
Base() {};
private:
Base(const Base &);
Base &operator=(const Base &);
};
struct Derived : Base {};
int main() {
Derived d1;
Derived d2(d1);
Derived d3;
d3 = d1;
}
用 g++ -std=c++03
编译会出现这些错误:
<source>: In copy constructor 'Derived::Derived(const Derived&)':
<source>:9:8: error: 'Base::Base(const Base&)' is private within this context
9 | struct Derived : Base {};
| ^~~~~~~
<source>:5:5: note: declared private here
5 | Base(const Base &);
| ^~~~
<source>: In function 'int main()':
<source>:13:18: note: synthesized method 'Derived::Derived(const Derived&)' first required here
13 | Derived d2(d1);
| ^
<source>: In member function 'Derived& Derived::operator=(const Derived&)':
<source>:9:8: error: 'Base& Base::operator=(const Base&)' is private within this context
9 | struct Derived : Base {};
| ^~~~~~~
<source>:6:11: note: declared private here
6 | Base &operator=(const Base &);
| ^~~~~~~~
<source>: In function 'int main()':
<source>:15:10: note: synthesized method 'Derived& Derived::operator=(const Derived&)' first required here
15 | d3 = d1;
| ^~
那时,“您的编译器会为您精心创建它”在技术上是正确的,但实际上并非如此,因为创建它的编译器会导致编译失败,只是出现不同的(并且可能不太清楚)错误。我现在确信,现在 = delete
被无条件使用,这根本不再是真的,所以我打算请 Qt 的维护者 remove/reword 他们文档的那一部分。