#include <new>
struct A { unsigned char buf[1]; };
static_assert(sizeof(A) == 1); // A can fit within A::buf
int main()
{
A x{};
new (x.buf) A{};
}
For example, does the new-expression in the program below
1) end the lifetime of x (by reusing its storage) and create another object in its place, or
2) create a new A object nested within x?
#include <new>
struct A { unsigned char buf[1]; };
static_assert(sizeof(A) == 1); // A can fit within A::buf
int main()
{
A x{};
new (x.buf) A{};
}
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+unsubscribe@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
On 11 September 2017 at 20:58, Chris Hallock <christoph...@gmail.com> wrote:For example, does the new-expression in the program below
1) end the lifetime of x (by reusing its storage) and create another object in its place, or
2) create a new A object nested within x?Under [intro.object]p3, the original object provides storage for the new object. Under [basic.life]p8, the name of the original object is rebound to the new object. But we did not meet the requirements of [basic.life]p5, so the original object's lifetime does not end. I think you end up with a "dangling" outer A object that can no longer be named, providing storage for an inner A object that is now named x.In terms of the observable behavior of the program, I think this is equivalent to replacing the original object with a new object.
On Tuesday, September 12, 2017 at 12:55:04 PM UTC-4, Richard Smith wrote:On 11 September 2017 at 20:58, Chris Hallock <christoph...@gmail.com> wrote:For example, does the new-expression in the program below
1) end the lifetime of x (by reusing its storage) and create another object in its place, or
2) create a new A object nested within x?Under [intro.object]p3, the original object provides storage for the new object. Under [basic.life]p8, the name of the original object is rebound to the new object. But we did not meet the requirements of [basic.life]p5, so the original object's lifetime does not end. I think you end up with a "dangling" outer A object that can no longer be named, providing storage for an inner A object that is now named x.In terms of the observable behavior of the program, I think this is equivalent to replacing the original object with a new object.
(Messed up my previous reply, trying again...)
But if the previous object didn't end, then rebinding doesn't happen — [basic.life]/8 starts "If, after the lifetime of an object has ended...".
What's unclear is whether the new-expression constitutes a re-use of x's storage or of x.buf's storage.
If the new-expression were new (&x) A{} instead of new (x.buf) A{}, does that change anything? I can't find any Standardese that clearly differentiates the behavior of these two expressions.
If nesting does indeed occur, that would seem to violate the assumed principle of the C++ object model that no two objects of the same type can exist simultaneously at the same address (besides unsigned char or std::byte, maybe).
std::launder() in particular appears to make that assumption.
--
On 12 September 2017 at 11:00, Chris Hallock <christoph...@gmail.com> wrote:On Tuesday, September 12, 2017 at 12:55:04 PM UTC-4, Richard Smith wrote:On 11 September 2017 at 20:58, Chris Hallock <christoph...@gmail.com> wrote:For example, does the new-expression in the program below
1) end the lifetime of x (by reusing its storage) and create another object in its place, or
2) create a new A object nested within x?Under [intro.object]p3, the original object provides storage for the new object. Under [basic.life]p8, the name of the original object is rebound to the new object. But we did not meet the requirements of [basic.life]p5, so the original object's lifetime does not end. I think you end up with a "dangling" outer A object that can no longer be named, providing storage for an inner A object that is now named x.In terms of the observable behavior of the program, I think this is equivalent to replacing the original object with a new object.
(Messed up my previous reply, trying again...)
But if the previous object didn't end, then rebinding doesn't happen — [basic.life]/8 starts "If, after the lifetime of an object has ended...".Good point. I think most of the above still applies: the lifetime rule in [basic.life]/1.4 requires that we first consider whether [intro.object]/3 applies. It does, and the buffer provides storage for the new object, so the lifetime of 'x' does not end. So 'x' is not rebound to the new object, and its buffer holds another X object.What's unclear is whether the new-expression constitutes a re-use of x's storage or of x.buf's storage.
If the new-expression were new (&x) A{} instead of new (x.buf) A{}, does that change anything? I can't find any Standardese that clearly differentiates the behavior of these two expressions.I agree. The wording in [intro.object] and [basic.life] concern themselves with the storage address represented by a pointer, not the object to which it points.If nesting does indeed occur, that would seem to violate the assumed principle of the C++ object model that no two objects of the same type can exist simultaneously at the same address (besides unsigned char or std::byte, maybe).That holds even for unsigned char / std::byte: if you placement new, say, a "struct A { unsigned char c; };" into an "unsigned char buffer[1];", the lifetime of the unsigned char object buffer[0] ends even though the lifetime of the buffer array itself does not.Perhaps Thiago's response is best: [intro.object]p3 should not apply if the array is nested within an object of the new object's type. The containing object must then necessarily be within its lifetime, have exactly overlapping storage, and contain no const or reference members, implying we instead reach either [intro.object]p2 (if that object is a subobject) or [basic.life]p8 (otherwise).
Would you like to file this as a defect report?
Would you like to file this as a defect report?I'd be happy to submit an issue report, if you're inviting me to (?).
Would you like to file this as a defect report?I'd be happy to submit an issue report, if you're inviting me to (?).I am :)
On 13 September 2017 at 09:14, Chris Hallock <christoph...@gmail.com> wrote:(Google Groups must be hungry, because it keeps eating my posts...)
On Tuesday, September 12, 2017 at 3:17:21 PM UTC-4, Richard Smith wrote:On 12 September 2017 at 11:00, Chris Hallock <christoph...@gmail.com> wrote:On Tuesday, September 12, 2017 at 12:55:04 PM UTC-4, Richard Smith wrote:On 11 September 2017 at 20:58, Chris Hallock <christoph...@gmail.com> wrote:For example, does the new-expression in the program below
1) end the lifetime of x (by reusing its storage) and create another object in its place, or
2) create a new A object nested within x?Under [intro.object]p3, the original object provides storage for the new object. Under [basic.life]p8, the name of the original object is rebound to the new object. But we did not meet the requirements of [basic.life]p5, so the original object's lifetime does not end. I think you end up with a "dangling" outer A object that can no longer be named, providing storage for an inner A object that is now named x.In terms of the observable behavior of the program, I think this is equivalent to replacing the original object with a new object.
(Messed up my previous reply, trying again...)
But if the previous object didn't end, then rebinding doesn't happen — [basic.life]/8 starts "If, after the lifetime of an object has ended...".Good point. I think most of the above still applies: the lifetime rule in [basic.life]/1.4 requires that we first consider whether [intro.object]/3 applies. It does, and the buffer provides storage for the new object, so the lifetime of 'x' does not end. So 'x' is not rebound to the new object, and its buffer holds another X object.What's unclear is whether the new-expression constitutes a re-use of x's storage or of x.buf's storage.
If the new-expression were new (&x) A{} instead of new (x.buf) A{}, does that change anything? I can't find any Standardese that clearly differentiates the behavior of these two expressions.I agree. The wording in [intro.object] and [basic.life] concern themselves with the storage address represented by a pointer, not the object to which it points.If nesting does indeed occur, that would seem to violate the assumed principle of the C++ object model that no two objects of the same type can exist simultaneously at the same address (besides unsigned char or std::byte, maybe).That holds even for unsigned char / std::byte: if you placement new, say, a "struct A { unsigned char c; };" into an "unsigned char buffer[1];", the lifetime of the unsigned char object buffer[0] ends even though the lifetime of the buffer array itself does not.Perhaps Thiago's response is best: [intro.object]p3 should not apply if the array is nested within an object of the new object's type. The containing object must then necessarily be within its lifetime, have exactly overlapping storage, and contain no const or reference members, implying we instead reach either [intro.object]p2 (if that object is a subobject) or [basic.life]p8 (otherwise).I agree that this should be prohibited. Would it suffice to add another constraint to [intro.object]/3? Something like "the created object would have an address different from any existing object of the same type (ignoring cv qualification)". Then new (x.buf) A{} would replace x instead of nest within, because we prohibited nesting.I think it'd be better to say something like "the array is not nested within an object of the same type (ignoring cv qualification) that is within its lifetime"