diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md
index e34aae7..bdfa7d3 100644
--- a/CppCoreGuidelines.md
+++ b/CppCoreGuidelines.md
@@ -4468,7 +4468,7 @@ Copy and move rules:
* [C.64: A move operation should move and leave its source in a valid state](#Rc-move-semantic)
* [C.65: Make move assignment safe for self-assignment](#Rc-move-self)
* [C.66: Make move operations `noexcept`](#Rc-move-noexcept)
-* [C.67: A base class should suppress copying, and provide a virtual `clone` instead if "copying" is desired](#Rc-copy-virtual)
+* [C.67: A polymorphic class should suppress copying](#Rc-copy-virtual)
Other default operations rules:
@@ -6046,59 +6046,68 @@ This `Vector2` is not just inefficient, but since a vector copy requires allocat
(Simple) A move operation should be marked `noexcept`.
-### C.67: A base class should suppress copying, and provide a virtual `clone` instead if "copying" is desired
+### C.67: A polymorphic class should suppress copying
##### Reason
-To prevent slicing, because the normal copy operations will copy only the base portion of a derived object.
+A *polymorphic class* is a class that defines or inherits at least one virtual function. It is likely that it will be used as a base class for other derived classes with polymorphic behavior. If it is accidentally passed by value, with the implicitly generated copy constructor and assignment, we risk slicing: only the base portion of a derived object will be copied, and the polymorphic behavior will be corrupted.
##### Example, bad
- class B { // BAD: base class doesn't suppress copying
- int data;
+ class B { // BAD: polymorphic base class doesn't suppress copying
+ public:
+ virtual char m() { return 'B'; }
// ... nothing about copy operations, so uses default ...
};
class D : public B {
- string more_data; // add a data member
+ public:
+ char m() override { return 'D'; }
// ...
};
- auto d = make_unique();
+ void f(B& b) {
+ auto b2 = b; // oops, slices the object; b2.m() will return 'B'
+ }
- // oops, slices the object; gets only d.data but drops d.more_data
- auto b = make_unique(d);
+ D d;
+ f(d);
##### Example
- class B { // GOOD: base class suppresses copying
+ class B { // GOOD: polymorphic class suppresses copying
public:
B(const B&) = delete;
B& operator=(const B&) = delete;
- virtual unique_ptr clone() { return /* B object */; }
+ virtual char m() { return 'B'; }
// ...
};
class D : public B {
- string more_data; // add a data member
- unique_ptr clone() override { return /* D object */; }
+ public:
+ char m() override { return 'D'; }
// ...
};
- auto d = make_unique();
- auto b = d.clone(); // ok, deep clone
+ void f(B& b) {
+ auto b2 = b; // ok, compiler will detect inadvertent copying, and protest
+ }
+
+ D d;
+ f(d);
##### Note
-It's good to return a smart pointer, but unlike with raw pointers the return type cannot be covariant (for example, `D::clone` can't return a `unique_ptr`. Don't let this tempt you into returning an owning raw pointer; this is a minor drawback compared to the major robustness benefit delivered by the owning smart pointer.
+If you need to create deep copies of polymorphic objects, use `clone()` functions: see [C.130](#Rh-copy).
##### Exception
-If you need covariant return types, return an `owner`. See [C.130](#Rh-copy).
+Classes that represent exception objects need both to be polymorphic and copy-constructible.
##### Enforcement
-A class with any virtual function should not have a copy constructor or copy assignment operator (compiler-generated or handwritten).
+* Flag a polymorphic class with a non-deleted copy operation.
+* Flag an assignment of polymorphic class objects.
## C.other: Other default operation rules
@@ -6496,7 +6505,7 @@ Designing rules for classes in a hierarchy summary:
* [C.127: A class with a virtual function should have a virtual or protected destructor](#Rh-dtor)
* [C.128: Virtual functions should specify exactly one of `virtual`, `override`, or `final`](#Rh-override)
* [C.129: When designing a class hierarchy, distinguish between implementation inheritance and interface inheritance](#Rh-kind)
-* [C.130: Redefine or prohibit copying for a base class; prefer a virtual `clone` function instead](#Rh-copy)
+* [C.130: For making deep copies of polymorphic classes prefer a virtual `clone` function to copy constructor](#Rh-copy)
* [C.131: Avoid trivial getters and setters](#Rh-get)
* [C.132: Don't make a function `virtual` without reason](#Rh-virtual)
* [C.133: Avoid `protected` data](#Rh-protected)
@@ -6991,35 +7000,32 @@ at the cost of the functionality being available only to users of the hierarchy.
* ???
-### C.130: Redefine or prohibit copying for a base class; prefer a virtual `clone` function instead
+### C.130: For making deep copies of polymorphic classes prefer a virtual `clone` function to copy constructor
##### Reason
-Copying a base is usually slicing. If you really need copy semantics, copy deeply: Provide a virtual `clone` function that will copy the actual most-derived type and return an owning pointer to the new object, and then in derived classes return the derived type (use a covariant return type).
+Copying a polymorphic class is discouraged due to the slicing problem, see [C.67](#Rc-copy-virtual). If you really need copy semantics, copy deeply: Provide a virtual `clone` function that will copy the actual most-derived type and return an owning pointer to the new object, and then in derived classes return the derived type (use a covariant return type).
##### Example
- class Base {
+ class B {
public:
- virtual owner clone() = 0;
- virtual ~Base() = 0;
+ virtual owner clone() = 0;
+ virtual ~B() = 0;
- Base(const Base&) = delete;
- Base& operator=(const Base&) = delete;
+ B(const B&) = delete;
+ B& operator=(const B&) = delete;
};
- class Derived : public Base {
+ class D : public B {
public:
- owner clone() override;
- virtual ~Derived() override;
+ owner clone() override;
+ virtual ~D() override;
};
-Note that because of language rules, the covariant return type cannot be a smart pointer. See also [C.67](#Rc-copy-virtual).
+Generally, it is recommended to use smart pointers to represent ownership (see [R.20](#Rr-owner)). However, because of language rules, the covariant return type cannot be a smart pointer: `D::clone` can't return a `unique_ptr` while `B::clone` returns `unique_ptr`. Therefore, you either need to consistently return `unique_ptr` in all overrides, or use `owner<>` utility from the [Guidelines Support Library](#SS-views).
-##### Enforcement
-* Flag a class with a virtual function and a non-user-defined copy operation.
-* Flag an assignment of base class objects (objects of a class from which another has been derived).
### C.131: Avoid trivial getters and setters
diff --git a/scripts/hunspell/isocpp.dic b/scripts/hunspell/isocpp.dic
index e893d5c..d6393c9 100644
--- a/scripts/hunspell/isocpp.dic
+++ b/scripts/hunspell/isocpp.dic
@@ -52,6 +52,8 @@ ASIC
asio
AST
async
+'B'
+b2
BDE
behaviorless
BigPOD