4

In the subsequent quotes, I'm referring to the ISO standard draft N4713.

§ 6.6.3, paragraph 1 says:

...The lifetime of an object o of type T ends when:
...
— the storage which the object occupies is released, or is reused by an object that is not nested within o (6.6.2).

Please answer the questions in the code comments:

#include <new>

int main() {
    int x[2] = {0, 1};
    char* p = new (x + 1) char{0}; // Has x ended its' lifetime?
    int z = x[0];                  // Is this UB?
}

If I used unsigned char as array element type, the array object x would have provided storage for the *p, according to § 6.6.2, paragraph 3:

If a complete object is created (8.5.2.4) in storage associated with another object e of type “array of N unsigned char” or of type “array of N std::byte” (21.2.1), that array provides storage for the created object if:
— the lifetime of e has begun and not ended, and
— the storage for the new object fits entirely within e, and
— there is no smaller array object that satisfies these constraints.

Please, validate my statements in the code comments:

#include <new>

int main() {
    unsigned char x[2] = {0, 1};
    char* p = new (x + 1) char{0}; // Only x[1] have ended its' lifetime.
    int z = x[0];                  // This is OK.
}

Although I do not understand the last rule in the previous quote, please give an example?

there is no smaller array object that satisfies these constraints.

7
  • § 6.6.2, paragraph 3 refers to an array of unsigned char or an array of std::byte. Your example has neither (your array if of int), so that section does not apply. Commented May 31, 2020 at 21:37
  • There seems to be a more fundamental flaw with your setup. § 6.6.3, paragraph 1 stipulates "that is not nested within", while § 6.6.2, paragraph 3 requires "the storage for the new object fits entirely within". That is, § 6.6.3, paragraph 1 applies when one object is not nested within another, while § 6.6.2, paragraph 3 applies only if one is nested within the other. The two paragraphs cannot both apply to your scenario. Commented May 31, 2020 at 21:45
  • "Although I do not understand the last rule in the previous quote, please give an example?" There is an example after the normative text timsong-cpp.github.io/cppwp/n4861/intro.object#3.example-1 Commented May 31, 2020 at 22:24
  • The lifetime of array ends, but int z = x[0]; seems not to be UB, because array-to-pointer conversion and pointer arithmetic don't require an array or its elements to be within their lifetime. And the lifetime of x[0] hasn't ended, because its storage wasn't reused. Commented May 31, 2020 at 22:30
  • 1
    There is no rule explicitly saying that «pointer arithmetic don't require an array or its elements to be within their lifetime», the rule about pointer arithmetic just doesn't depend on lifetime. Commented Jun 1, 2020 at 19:24

1 Answer 1

0

According to the following reading of the standard, the array lifetime is not ended by the placement new expression:

[basic.life]/1.5:

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

  • [...]

  • the storage which the object occupies is released, or is reused by an object that is not nested within o ([intro.object]).

According to this rule, if the new object created within the array is not nested within the array, the array life time end. Otherwise it is not ended.

[intro.object]/2

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:

  • the lifetime of e's containing object has begun and not ended, and

  • the storage for the new object exactly overlays the storage location associated with e, and

  • the new object is of the same type as e (ignoring cv-qualification).

The two last bullets are not full-filled, so obviously, the new char object is not a suboject of the array.

Then [intro.object]/3:

If a complete object is created ([expr.new]) in storage associated with another object e of type “array of N unsigned char” or of type “array of N std​::​byte” ([cstddef.syn]), that array provides storage for the created object if:

  • the lifetime of e has begun and not ended, and

  • the storage for the new object fits entirely within e, and

  • there is no smaller array object that satisfies these constraints.

So the array of unsigned char provides storage for the new char object.

[intro.object]/4:

An object a is nested within another object b if:

  • a is a subobject of b, or

  • b provides storage for a, or

  • there exists an object c where a is nested within c, and c is nested within b.

As we mentioned above, the first bullet is not full-filled. The second one is full-filled. So according to [basic.life]/1.5, the lifetime of the array is not ended because the new char object is nested within the array:

int main() {
    unsigned char x[2] = {0, 1};
    char* p = new (x + 1) char{0}; // x provides storage for the char object
    int z = x[0];                  // OK The first element is within its lifetime.
}

If it were not the case, probably that most of the low-level code would not be standard compliant!

Sign up to request clarification or add additional context in comments.

1 Comment

are you sure that the lifetime is not ended? it is not really 100% clear what "provides storage" means in the definition of the nested object. For example, the standard clearly says that only std::byte or unsigned chart provides storage: eel.is/c++draft/basic.memobj#intro.object-3. But your answer assumes that it just covers any storage.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.