-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Add C.13 to ensure data member lifetimes nest when needed... see #2316 #2321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -4671,6 +4671,7 @@ Concrete type rule summary: | |||||
| * [C.10: Prefer concrete types over class hierarchies](#rc-concrete) | ||||||
| * [C.11: Make concrete types regular](#rc-regular) | ||||||
| * [C.12: Don't make data members `const` or references in a copyable or movable type](#rc-constref) | ||||||
| * [C.13: If one data member uses another, declare it before the other](#rc-lifetime) | ||||||
|
|
||||||
|
|
||||||
| ### <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 | |||||
| Flag a data member that is `const`, `&`, or `&&` in a type that has any copy or move operation. | ||||||
|
|
||||||
|
|
||||||
| ### <a name="rc-lifetime"></a>C.13: If one data member uses another, declare it before the other | ||||||
|
|
||||||
| ##### Reason | ||||||
|
|
||||||
| Data members are initialized in the order they are declared, and destroyed in the reverse order. | ||||||
|
|
||||||
| ##### Discussion | ||||||
|
|
||||||
| 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. | ||||||
|
|
||||||
| ##### Example; bad | ||||||
|
|
||||||
| // Bad: b uses a, but a is declared after b. | ||||||
| // Construction order is b then a; destruction order is a then b. | ||||||
| // So b touches a outside a's lifetime. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like the build failed because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it could also rephrase this line to "So b touches a outside the lifetime of a.", which conveys the same meaning. I think this phrasing might also express the intent more clearly, I'm not entirely sure |
||||||
|
|
||||||
| class X { | ||||||
| struct B { | ||||||
| string* p; | ||||||
| explicit B(string& a) : p(&a) {} | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| ~B() { cout << *p; } // uses a (via p) | ||||||
| }; | ||||||
|
|
||||||
| B b; // constructed first | ||||||
| string a = "some heap allocated string value"; // constructed after b; destroyed before b | ||||||
|
|
||||||
| public: | ||||||
| X() : b(a) {} // uses a before it is constructed -> use-before-alloc UB | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| ~X() = default; // accesses a after it is destroyed -> use-after-free UB | ||||||
| }; | ||||||
|
|
||||||
| ##### Example; good | ||||||
|
|
||||||
| // Corrected: Just declare a before b | ||||||
|
|
||||||
| class X { | ||||||
| struct B { | ||||||
| string* p; | ||||||
| explicit B(string& a) : p(&a) {} | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| ~B() { cout << *p; } // uses a (via p) | ||||||
| }; | ||||||
|
|
||||||
| string a = "some heap allocated string value"; // constructed before b; destroyed after b | ||||||
| B b; // constructed second | ||||||
|
|
||||||
| public: | ||||||
| X() : b(a) {} // ok | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| ~X() = default; // ok | ||||||
| }; | ||||||
|
|
||||||
| ##### Example; bad | ||||||
|
|
||||||
| 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. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| class X { | ||||||
| public: | ||||||
| X() | ||||||
| : a(std::make_unique<int>(12)) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| { | ||||||
| b = std::make_unique<std::jthread>( | ||||||
| [this]{ | ||||||
| std::this_thread::sleep_for(std::chrono::seconds(1)); | ||||||
| std::cout << "Value: " << *a << std::endl; | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| std::unique_ptr<std::jthread> b; | ||||||
| std::unique_ptr<int> a; | ||||||
| }; | ||||||
|
|
||||||
| ##### Example; good | ||||||
|
|
||||||
| 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. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| // Corrected: Just declare a before b | ||||||
|
|
||||||
| class X { | ||||||
| public: | ||||||
| X() | ||||||
| : a(std::make_unique<int>(12)) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| { | ||||||
| b = std::make_unique<std::jthread>( | ||||||
| [this]{ | ||||||
| std::this_thread::sleep_for(std::chrono::seconds(1)); | ||||||
| std::cout << "Value: " << *a << std::endl; | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| std::unique_ptr<int> a; | ||||||
| std::unique_ptr<std::jthread> b; | ||||||
| }; | ||||||
|
|
||||||
| ##### Enforcement | ||||||
|
|
||||||
| * Flag a member initializer that refers to an object before it is constructed. | ||||||
|
|
||||||
|
|
||||||
| ## <a name="s-ctor"></a>C.ctor: Constructors, assignments, and destructors | ||||||
|
|
||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My reading of the title is (italic text is mine):
This is in contrast to what the Discussion section says:
I am not a native English speaker. Is my reading of the title wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your interpretation of the title and that they contradict.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe title could be 'If one data member uses another, declare it after the used member' ?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think maybe it could rephrase to 'Declare a data member before members that depend on it' — it should convey roughly the same meaning?