Make C.43 crisper -- the guideline is that default construction is
required for copyable types. A lot of the existing examples then just
fall out, without having to be presented as special cases. This was the
original intent and I think this new text helps make that clearer.
This commit is contained in:
hsutter 2018-02-12 16:03:01 -08:00
parent d9f3149e11
commit 10c0a8b156

View File

@ -4409,7 +4409,7 @@ Constructor rules:
* [C.40: Define a constructor if a class has an invariant](#Rc-ctor)
* [C.41: A constructor should create a fully initialized object](#Rc-complete)
* [C.42: If a constructor cannot construct a valid object, throw an exception](#Rc-throw)
* [C.43: Ensure that a value type class has a default constructor](#Rc-default0)
* [C.43: Ensure that a copyable (value type) class has a default constructor](#Rc-default0)
* [C.44: Prefer default constructors to be simple and non-throwing](#Rc-default00)
* [C.45: Don't define a default constructor that only initializes data members; use member initializers instead](#Rc-default)
* [C.46: By default, declare single-argument constructors `explicit`](#Rc-explicit)
@ -5096,17 +5096,16 @@ Another reason has been to delay initialization until an object is needed; the s
???
### <a name="Rc-default0"></a>C.43: Ensure that a value type class has a default constructor
### <a name="Rc-default0"></a>C.43: Ensure that a copyable (value type) class has a default constructor
##### Reason
Many language and library facilities rely on default constructors to initialize their elements, e.g. `T a[10]` and `std::vector<T> v(10)`.
A default constructor often simplifies the task of defining a suitable [moved-from state](#???).
A default constructor often simplifies the task of defining a suitable [moved-from state](#???) for a type that is also copyable.
##### Note
We have not (yet) formally defined [value type](#SS-concrete), but think of it as a class that behaves much as an `int`:
it can be copied using `=` and usually compared using `==`.
A [value type](#SS-concrete) is a class that is copyable (and usually also comparable).
It is closely related to the notion of Regular type from [EoP](http://elementsofprogramming.com/) and [the Palo Alto TR](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3351.pdf).
##### Example
@ -5178,41 +5177,47 @@ Assuming that you want initialization, an explicit default initialization can he
int i {}; // default initialize (to 0)
};
##### Example
##### Notes
There are classes that simply don't have a reasonable default.
Classes that don't have a reasonable default construction are usually not copyable either, so they don't fall under this guideline.
A class designed to be useful only as a base does not need a default constructor because it cannot be constructed by itself:
For example, a base class is not a value type (base classes should not be copyable) and so does not necessarily need a default constructor:
struct Shape { // pure interface: all members are pure virtual functions
void draw() = 0;
void rotate(int) = 0;
// ...
// Shape is an abstract base class, not a copyable value type.
// It may or may not need a default constructor.
struct Shape {
virtual void draw() = 0;
virtual void rotate(int) = 0;
// =delete copy/move functions
// ...
};
A class that must acquire a resource during construction:
A class that must acquire a caller-provided resource during construction often cannot have a default constructor, but it does not fall under this guideline because such a class is usually not copyable anyway:
// std::lock_guard is not a copyable value type.
// It does not have a default constructor.
lock_guard g {mx}; // guard the mutex mx
lock_guard g2; // error: guarding nothing
##### Note
A class that has a "special state" that must be handled separately from other states by member functions or users causes extra work
(and most likely more errors). For example
(and most likely more errors). Such a type can naturally use the special state as a default constructed value, whether or not it is copyable:
// std::ofstream is not a copyable value type.
// It does happen to have a default constructor
// that goes along with a special "not open" state.
ofstream out {"Foobar"};
// ...
out << log(time, transaction);
If `Foobar` couldn't be opened for writing and `out` wasn't set to throw exceptions upon errors, the output operations become no-ops.
The implementation must take care of that case, and users must remember to test for success.
Similar special-state types that are copyable, such as copyable smart pointers that have the special state "==nullptr", should use the special state as their default constructed value.
Pointers, even smart pointers, that can point to nothing (null pointers) are an example of this.
Having a default constructor is not a panacea; ideally it defaults to a meaningful state such as `std::string`s `""` and `std::vector`s `{}`.
However, it is preferable to have a default constructor default to a meaningful state such as `std::string`s `""` and `std::vector`s `{}`.
##### Enforcement
* Flag classes that are copyable by `=` or comparable with `==` without a default constructor
* Flag classes that are copyable by `=` without a default constructor
* Flag classes that are comparable with `==` but not copyable
### <a name="Rc-default00"></a>C.44: Prefer default constructors to be simple and non-throwing