2

Regarding the code below

int main()
{
    struct S { int i; } s { 42 };
    new (&s.i) int { 43 };
}

[basic.stc]/2 says

The dynamic storage duration is associated with objects created by a new-expression.

There is similar question where it was agreed that placement new creates objects with dynamic storage duration. Because there is no wording saying otherwise which would be applicable to the example in that question.

Here is a carefully crafted example for which there is wording saying something interesting. [basic.stc.inherit]/1 says:

The storage duration of subobjects and reference members is that of their complete object

and [intro.object]/2 guarantees that the created int object is a subobject of s:

If an object is created in storage associated with a member subobject or array element e (which may or may not be within its lifetime), the created object is a subobject of e's containing object if:
(requirements are satisfied, I won't copy them here)

So, which storage duration the newly-created int object has? Dynamic or automatic?

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
Language Lawyer
  • 3,378
  • 1
  • 12
  • 29

1 Answers1

1

This question is very interesting. It is true that the wording about dynamic storage duration and new expressions does not exclude the placement-new.

The ambiguity in the formulation is caused by the great variety of cases that need to be covered:

int main()
{
    struct Simple { int i; } s { 42 };
    new (&s.i) int { 43 };   // the lifecyle of the newly created object 
                             // will auto, since Simple will be destroyed when
                             // going out of scope
    struct Complex { char s[256]; } c; 
    Simple *p = new (&c.s) Simple;  // the lifecycle of the newly created object 
                            // is dynamic.  You'll need to delete it in time. 
                            // because compiler doesn't know about its true nature
                            // and memory will be lost when going out of scope
}   

The standard is fortunately precise enough, so that if you provide the right code (i.e. no UB), every compiler will produce the same result.

In fact, dynamic creation of an object (placement new) does not necessarily imply that the object lifecycle is independent of the scope in which the object is created. But it is up to you to make sure that the object will be destroyed in due time, and therefore it is considered as dynamic storage duration.

The key here is the allocation. Only memory allocation can make an object duration truely independent of the scope in which it was created. This is expressed in the standard, but maybe not as clearly as it could have be. Let's start with the full clause in basic.stc/2:

Static, thread, and automatic storage durations are associated with objects introduced by declarations and implicitly created by the implementation. The dynamic storage duration is associated with objects created by a new-expression.

I'd understand here that the last sentence does only apply if the object is not already covered by the first sentence. But this is a personal interpretation for the moment. So the only thing for sure is that in case of overlap, extra care is needed.

So let's look more closely at Dynamic storage duration [ basic.stc.dynamic ]/1

Objects can be created dynamically during program execution (...). A C++ implementation provides access to, and management of, dynamic storage via the global allocation functions operator new and operator new[] and the global deallocation functions operator delete and operator delete[]. [ Note: The non-allocating forms described in 21.6.2.3 do not perform allocation or deallocation. — end note ]

The second sentence makes clear that dynamic storage means allocation. Then comes the interesting note, which refers to exactly the chapter [new.delete.placement]/1:

These functions are reserved; a C++ program may not define functions that displace the versions in the C++ standard library. The provisions of 6.7.4 do not apply to these reserved placement forms of operator new and operator delete.

The section 6.7.4 is the section basic.stc.dynamic. This means that the special allocation used for placement-new does not create dynamic storage.

THe fact that dynamic storage and dynamic storage duration are not one and the same thing makes the whole stuff difficult to express:

  • dynamic storage duration means that you have to take care for the object lifecycle and delete when necessary
  • dynamic storage means that there are no constraints to the storage duration.
  • creating a dynamic storage duration object elsewhere than in dynamic storage (and especially in an automatic storage location) requires extra care, since you need to make sure it is destroyed while the storage is available. If you just replace an object with an object of the same type in a placement-new, you'll benefit from the code that will destroy the object when leaving the scope. But in any other case, you'll need to take care.

Here an online demo to play with placement-new and destruction, and to see what happens when the enclosing object goes out of scope. It uses a Tracer class that will highlight better than an int the different cases (including deletion of the previous object before invoking a placement new).

Conclusion: I think that some ambiguity and circularity can't be avoided in any standard with such a long history and with so many contributors. But in this case, you can see that the matter itself has more facets that you'd have expected in first place.

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • If I replace the global allocation functions to return memory from a predefined global character array, will it be access to dynamically allocated storage? – Language Lawyer Feb 17 '19 at 18:08
  • @LanguageLawyer that's yet another case. It's dynamic memory duration for the objects that are created this way, since you need to take care of deletion. That's sure. Spontaneously, I'd say it's not dynamic storage, since it's in the global memory (static storage) that you package in smaller pieces. On the other hand, you achieve exacly the same behavior than when allocating from the free store. So I'm not sure. It's a tricky question and in the short run, I'd use a Westworld like answer: "If you can't make the difference, does it matter ?" ;-) – Christophe Feb 17 '19 at 18:25