1

According to [basic.life/1] (bold emphasis mine):

The lifetime of an object of type T begins when:

  • storage with the proper alignment and size for type T is obtained, and
  • its initialization (if any) is complete (including vacuous initialization) ([dcl.init]), except that if the object is a union member or subobject thereof, its lifetime only begins if that union member is the initialized member in the union ([dcl.init.aggr], [class.base.init]), or as described in [class.union] and [class.copy.ctor], and except as described in [allocator.members].

The lifetime of an object o of type T ends when:

  • if T is a non-class type, the object is destroyed, or
  • if T is a class type, the destructor call starts, or
  • the storage which the object occupies is released, or is reused by an object that is not nested within o ([intro.object]).

So we have:

|------------|----|--------------|----------|-------------|----|--------------|

 <---------->      <------------> <--------> <----------->      <------------>
  storage           object         object     object             storage
  allocation        construction   lifetime   destruction        deallocation

              <----------------------------------------------->
               storage duration

According to [basic.life/8] (bold emphasis mine):

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if the original object is transparently replaceable (see below) by the new object.

Two questions come to mind:

  1. Is the placement new operator the only means to reuse storage?
  2. Does the consequence specified in [basic.life/8] really require the original object to be destroyed (i.e. the condition ‘after the lifetime of an object has ended’)?
Géry Ogam
  • 6,336
  • 4
  • 38
  • 67
  • 1
    2. It does, but should not, because this requirement makes the unions unusable in C++ (if a union member was not originally active, it is not possible to make it active, since creating an object of member's type won't make the member's name refer to the new [sub]object) – Language Lawyer Sep 13 '21 at 15:14
  • @LanguageLawyer Do you mean it makes the statement `name.~basic_string();` required in the member assignment operator in the code of [this answer](https://stackoverflow.com/a/69159423/2326961)? – Géry Ogam Sep 13 '21 at 15:33
  • @Maggyero: That's required because you *need* to destroy the object, since it is a non-trivial destructor that your code is relying on being called to free up memory. – Nicol Bolas Sep 13 '21 at 15:39
  • 2
    No, object's lifetime ends just from storage reuse. This is enough for [basic.life]/8. Mebby my first comment is confusing. Explicit destructor call is not required, but neither lifetime end should be. – Language Lawyer Sep 13 '21 at 15:50
  • @LanguageLawyer About 2, okay so we agree that the condition ‘after the lifetime of an object has ended’ should be removed from the standard because the lifetime of the orignal object will end anyway from storage reuse. – Géry Ogam Sep 13 '21 at 20:39
  • No, it should be removed because sometimes lifetime of the old object doesn't even start (so it can't be ended), but a name needa be rebound to the new object. – Language Lawyer Sep 13 '21 at 21:08
  • @LanguageLawyer Let’s clarify: A. [basic.life/1] specifies that storage reuse, storage release, or object destruction ends an object’s lifetime. B. [basic.life]/8 specifies that if an object’s lifetime ends *before* storage reuse or storage release, storage can be reused for a new object. Therefore from A and B, if an object is destroyed *before* storage reuse or storage release, storage can be reused for a new object. Now I think there are two problems with this statement: 1. This does not make sense: ‘*before* storage reuse’. 2. This is unnecessary: ‘if an object is destroyed’. Do you agree? – Géry Ogam Sep 13 '21 at 22:24
  • See https://timsong-cpp.github.io/cppwp/n4868/basic.life#11 what «before» and «after» mean and what they are needed for. – Language Lawyer Sep 13 '21 at 22:26
  • @LanguageLawyer The problem is not the meaning of before, it is: before storage reuse, storage can be reused. It implies that storage can only be reused once, which is insane. – Géry Ogam Sep 13 '21 at 22:31
  • _It implies that storage can only be reused once_ I don't see this. – Language Lawyer Sep 14 '21 at 16:15
  • @LanguageLawyer Basically it says: *if you create an object in storage where an object whose lifetime ended and the storage hasn’t been reused/released, then…* But why doesn’t it just say: *if you create an object in storage where the storage hasn’t been released, then…* In other words, wouldn’t the consequence on pointers/references/names referring to the object still apply with this less strict condition? – Géry Ogam Sep 14 '21 at 19:58
  • `int i; auto t1 = std::thread([&] { new (&i) int{1}; }), t2 = std::thread([&] { new (&i) int{2}; }); t1.join(); t2.join();` which object does `i` denote now? – Language Lawyer Sep 14 '21 at 20:07
  • @LanguageLawyer There is a race condition so I don’t know. What did you want to show? – Géry Ogam Sep 14 '21 at 21:58
  • What is «race condition»? If you mean «data race», there is none in the code fragment. – Language Lawyer Sep 14 '21 at 22:00
  • @LanguageLawyer I mean that both threads are updating the same storage location concurrently. – Géry Ogam Sep 14 '21 at 22:16
  • What is «storage location»? If you mean «memory location», they are not the same. (And I'd say there are no updates) – Language Lawyer Sep 14 '21 at 22:17
  • @LanguageLawyer I don’t know the difference. Yes the two threads are not ‘updating’ the value of an object at a storage location, they are ‘creating’ two objects at a storage location. But what point did you want to make with this example? – Géry Ogam Sep 15 '21 at 05:35

1 Answers1

0

As you quoted, if you reuse storage for a new object, the old object(s) in that storage have their lifetimes ended (unless the new object is nested within the old one). So if you create a new object inside storage containing an existing object(s), then the existing object(s) won't exist anymore.

Is the placement new operator the only means to reuse storage?

The word "reuse" is not a term of art; it's just English. Anything that creates an object in storage will "reuse" the storage. For example, construct_at can reuse storage too.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • `construct_at` is basically just a wrapper around placement new: http://eel.is/c++draft/specialized.construct#2. – Daniel Langr Sep 13 '21 at 15:28
  • All the ways how a new object may be created are here: http://eel.is/c++draft/intro.object#1.sentence-2. – Daniel Langr Sep 13 '21 at 15:40
  • About 2, so do you agree that the condition ‘after the lifetime of an object has ended’ should be removed from the standard because the lifetime of the orignal object will end anyway from storage reuse? – Géry Ogam Sep 13 '21 at 20:40
  • About 1, what C++ constructs currently allow storage reuse besides placement `new` and `construct_at`? – Géry Ogam Sep 13 '21 at 20:42
  • @Maggyero: "*do you agree that the condition ‘after the lifetime of an object has ended’ should be removed from the standard because the lifetime of the orignal object will end anyway from storage reuse?*" No, because it's talking specifically about a pointer/reference to, or the name of, a specific object. Noting that this object's lifetime has ended is part of that. – Nicol Bolas Sep 13 '21 at 20:49
  • I am not sure I understand: since reusing an object’s storage ends its lifetime, why forcing the programmer to destroy it before storage reuse? – Géry Ogam Sep 13 '21 at 21:47
  • @Maggyero: You're misreading the text. It's not saying "to have effect Y, you must do X". It's saying "when X happens, then Y happens." Nobody is *forced* to do anything; this just explains what happens when these events take place. – Nicol Bolas Sep 13 '21 at 21:57
  • Let’s clarify: A. [basic.life/1] specifies that storage reuse, storage release, or object destruction ends an object’s lifetime. B. [basic.life]/8 specifies that if an object’s lifetime ends *before* storage reuse or storage release, storage can be reused for a new object. Therefore from A and B, if an object is destroyed *before* storage reuse or storage release, storage can be reused for a new object. Now I think there are two problems with this statement: 1. ‘before storage reuse’ implies that storage can only be reused once. 2. ‘if an object is destroyed’ is unnecessary. Do you agree? – Géry Ogam Sep 13 '21 at 22:34
  • @Maggyero: "*if an object’s lifetime ends before storage reuse or storage release, storage can be reused for a new object.*" No, that is *not* what it says. Notice the comma after the first "If" in the paragraph. The comma-delineated section is a separate conditional. That is, it says "if you do this thing" (create an object) "in storage where this other thing is true" (an object whose lifetime ended and the storage hasn't been reused/released), then ... – Nicol Bolas Sep 13 '21 at 23:34
  • @Maggyero: This is not *defining* what it means to "reuse storage". It's simply laying out what happens to pointers/references/names to an old object relative to a new object under certain conditions. – Nicol Bolas Sep 13 '21 at 23:36
  • Yes it says: *if you create an object in storage where an object whose lifetime ended and the storage hasn’t been reused/released, then…* But why doesn’t it just say: *if you create an object in storage where the storage hasn’t been released, then…* In other words, wouldn’t the consequence on pointers/references/names referring to the object still apply with this less strict condition? – Géry Ogam Sep 14 '21 at 06:23