1

Is the operation of rewriting the value of object3 by the following source code valid in the current C++ standard?

#include <iostream>

void foreign_function(short* s);

int main() {
    alignas(8) char object1[16];

    short* p_object2 = new(object1 + 2) short(10);  // Create object2 of type short
    long* p_object3 = new(object1 + 8) long(100);   // Create object3 of type long

    foreign_function(p_object2);

    std::cout << *p_object3  << std::endl;
}

void foreign_function(short* s) {
    char* p_object4 = new(s) char();  // Create object4 of type char
    char* pointer5 = p_object4 + 6;   // (*) pointer arithmetic
    new(pointer5) long(1000);  // object3 is transparently replaceable by the new object ([basic.life] p8)
}

In particular, I am wondering if the pointer operation indicated by (*) is valid.

When object2 is created, object1 provides storage for object2 according to the [intro.object] p3, and therefore lifetime of object1 does not end.

https://timsong-cpp.github.io/cppwp/n4861/intro.object#3

https://timsong-cpp.github.io/cppwp/n4861/intro.object#4.2

https://timsong-cpp.github.io/cppwp/n4861/basic.memobj#basic.life-1.5

However, the lifetime of the second and third elements of object1, objects of type char, ends. The same is true when creating object3.

Then, when object4 of type char is created, the lifetime of object2 ends. In this case, object4 becomes a subobject of object1 according to [intro.object] p2. The element of object1 that once existed at the same address as object4, i.e., the second element of object1, has already reached the end of its lifetime, but according to [intro.object] p2 that should not be a problem.

https://timsong-cpp.github.io/cppwp/n4861/intro.object#2

It seems to me that p_object4 can be used for pointer arithmetic according to [expr.add] p4.2.

https://timsong-cpp.github.io/cppwp/n4861/expr.add#4.2

However, [intro.object] p2 only specifies that object4 is a subobject of object1. Can object4 really be properly treated as the second element of object1?

https://timsong-cpp.github.io/cppwp/n4861/dcl.array#6

8
  • 1
    What is a practical usage of such unreadable code? Just curiosity? Commented Oct 31, 2023 at 15:34
  • For example, I am thinking of an allocator that uses boundary tags. If such operations are prohibited, it would be difficult to subtract an address from the pointer of the object given to deallocate to get the previous tag without UB. PS: std::launder is required to access the previous tag after subtracting the address. Commented Oct 31, 2023 at 15:40
  • just looking at that line you marked with (*), yes it is valid, there is nothing wrong with adding to a char pointer Commented Oct 31, 2023 at 15:42
  • 1
    @hogeegoh: Not commenting on your actual question but allocators are often provided by the implementation, and that is allowed to violate all sorts of rules (under the assumption that an implementer knows enough to make their otherwise UB code result in the outcome demanded by the standard). Commented Oct 31, 2023 at 15:51
  • 1
    new(pointer5) long(1000); may cause alignment issues though. Commented Oct 31, 2023 at 15:52

1 Answer 1

1

However, [intro.object] p2 only specifies that object4 is a subobject of object1. Can object4 really be properly treated as the second element of object1?

First of all, object4 must be an array element, because [intro.object]/2 specifies that subobjects are member subobjects, base class subobjects, or array elements. And since object4 can only be an array element, which one is it? Well, according to [dcl.array]/6 the only elements of object1 are numbered 0 through 15, and object4 happens to occupy the storage allocated for the element with index 2, so how could it be anything else?

But I think there's something fishy about this code: it defeats the compiler's reachability analysis, whereby when a pointer to a float object is passed to foreign_function, the latter should only be able to use that pointer to affect bytes that are reachable from that float object. But if object4 is truly an element of object1, then a pointer to object4 certainly can be used to access any byte in object1. So it seems like the current wording needs to be amended to prevent this from happening somehow, but I'm not sure about the correct direction. I'll raise this issue with the Core Working Group.

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

5 Comments

For reference: I asked whether placement-new contradicts the reachability conditions here and @LanguageLaywer mentioned that he had brought this up before. In this question the method used to circumvent reachability is a bit different, but the simpler approach is also still inconsistent with std::launder.
Thank you very much! Your inquiry to CWG would be greatly appreciated. I, too, was concerned by the inconsistency in the reachability analysis and wondered if the code was in fact UB, and that is why I asked this question.
By the way, I also have a few questions about [dcl.array] p6. Is it talking about subobjects that are "within their lifetime" or subobjects that are "regardless of their lifetime"? If the former, then the "N subobjects" is inconsistent, since some subobjects end their lifetime when object2 or objest3 is created. If the latter, then new subobjects would be added when object4 is created, which would read as a contradiction.
@hogeegoh It means "regardless of lifetime". If you end the lifetime of one of the array elements by reusing the storage, it's still there, but not within its lifetime. If you then reuse the storage again to recreate an object of the element type, the new object transparently replaces the one that used to be there.
@Brian Bi I think that "transparently replaceable" is a pointer, reference, and name convention, and that there is no particular change in the "replaced" object itself. That is, the original second subobject of the object1, the char object whose lifetime has ended, remains a subobject of object1 even after being "transparently replaced" by object4? In that case, there would be 17 subobjects of object1. That being said, this is nitpicking and overall I agree with your opinion.

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.