Skip to content

Commit 063ee83

Browse files
committed
Add C.13 to ensure data member lifetimes nest when needed... see #2316
Closes #2316
1 parent 1731b46 commit 063ee83

1 file changed

Lines changed: 97 additions & 0 deletions

File tree

CppCoreGuidelines.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4671,6 +4671,7 @@ Concrete type rule summary:
46714671
* [C.10: Prefer concrete types over class hierarchies](#rc-concrete)
46724672
* [C.11: Make concrete types regular](#rc-regular)
46734673
* [C.12: Don't make data members `const` or references in a copyable or movable type](#rc-constref)
4674+
* [C.13: If one data member uses another, declare it before the other](#rc-lifetime)
46744675

46754676

46764677
### <a name="rc-concrete"></a>C.10: Prefer concrete types over class hierarchies
@@ -4792,6 +4793,102 @@ If you need a member to point to something, use a pointer (raw or smart, and `gs
47924793
Flag a data member that is `const`, `&`, or `&&` in a type that has any copy or move operation.
47934794

47944795

4796+
### <a name="rc-lifetime"></a>C.13: If one data member uses another, declare it before the other
4797+
4798+
##### Reason
4799+
4800+
Data members are initialized in the order they are declared, and destroyed in the reverse order.
4801+
4802+
##### Discussion
4803+
4804+
If data member `B` uses another data member `A`, then `A` must be declared before `B` so that `A` outlives `B`, meaning that `A`'s lifetime starts before and ends after `B`'s lifetime. Otherwise, during construction and destruction `B` will attempt to use `A` outside its lifetime.
4805+
4806+
##### Example; bad
4807+
4808+
// Bad: b uses a, but a is declared after b.
4809+
// Construction order is b then a; destruction order is a then b.
4810+
// So b touches a outside a's lifetime.
4811+
4812+
class X {
4813+
struct B {
4814+
string* p;
4815+
explicit B(string& a) : p(&a) {}
4816+
~B() { cout << *p; } // uses a (via p)
4817+
};
4818+
4819+
B b; // constructed first
4820+
string a = "some heap allocated string value"; // constructed after b; destroyed before b
4821+
4822+
public:
4823+
X() : b(a) {} // uses a before it is constructed -> use-before-alloc UB
4824+
~X() = default; // accesses a after it is destroyed -> use-after-free UB
4825+
};
4826+
4827+
##### Example; good
4828+
4829+
// Corrected: Just declare a before b
4830+
4831+
class X {
4832+
struct B {
4833+
string* p;
4834+
explicit B(string& a) : p(&a) {}
4835+
~B() { cout << *p; } // uses a (via p)
4836+
};
4837+
4838+
string a = "some heap allocated string value"; // constructed before b; destroyed after b
4839+
B b; // constructed second
4840+
4841+
public:
4842+
X() : b(a) {} // ok
4843+
~X() = default; // ok
4844+
};
4845+
4846+
##### Example; bad
4847+
4848+
This can also come up with concurrency. Ensure that an async operation that access a value is joined before the value it accesses is destroyed.
4849+
4850+
class X {
4851+
public:
4852+
X()
4853+
: a(std::make_unique<int>(12))
4854+
{
4855+
b = std::make_unique<std::jthread>(
4856+
[this]{
4857+
std::this_thread::sleep_for(std::chrono::seconds(1));
4858+
std::cout << "Value: " << *a << std::endl;
4859+
});
4860+
}
4861+
4862+
std::unique_ptr<std::jthread> b;
4863+
std::unique_ptr<int> a;
4864+
};
4865+
4866+
##### Example; good
4867+
4868+
This can also come up with concurrency. Ensure that an async operation that access a value is joined before the value it accesses is destroyed.
4869+
4870+
// Corrected: Just declare a before b
4871+
4872+
class X {
4873+
public:
4874+
X()
4875+
: a(std::make_unique<int>(12))
4876+
{
4877+
b = std::make_unique<std::jthread>(
4878+
[this]{
4879+
std::this_thread::sleep_for(std::chrono::seconds(1));
4880+
std::cout << "Value: " << *a << std::endl;
4881+
});
4882+
}
4883+
4884+
std::unique_ptr<int> a;
4885+
std::unique_ptr<std::jthread> b;
4886+
};
4887+
4888+
##### Enforcement
4889+
4890+
* Flag a member initializer that refers to an object before it is constructed.
4891+
47954892

47964893
## <a name="s-ctor"></a>C.ctor: Constructors, assignments, and destructors
47974894

0 commit comments

Comments
 (0)