1

Consider the following code:

#include <string>
struct S { std::string str; };

int main() {
    S *data = static_cast<S*>(operator new(sizeof(S) * 3));
    new (&data[1]) S();  // (1)
    new (data + 2) S();  // (2)
    data[2].~S();  // (3)
    data[1].~S();  // (4)
    operator delete(data);
}

My understanding is that lines (2)-(4) have perfectly valid behavior.

However, I'm not sure about line (1): on the one hand, I never access the non-existing object data[1] because I only need its address. On the other hand, I do by writing data[1], thus possibly invoking undefined behavior.

Is &data[1] defined when data is an allocated chunk of memory, but there is no object or subobject at data + 1?

yeputons
  • 8,478
  • 34
  • 67
  • Aside why `operator new` and `operator delete`? – Caleth Feb 25 '21 at 09:54
  • @Caleth No particular reason. C++-isher than `malloc`/`free` and I have not used `std::allocator` much. – yeputons Feb 25 '21 at 09:56
  • 2
    I mean instead of `new` and `delete` – Caleth Feb 25 '21 at 09:57
  • @Caleth So I get raw memory, not an array of initialized objects like in `new S[3]`. `new (3)` is not valid C++. – yeputons Feb 25 '21 at 09:59
  • 1
    IMHO raw memory might not be aligned sufficiently. Might want to use aligned_storage at line 0. – rustyx Feb 25 '21 at 10:24
  • 1
    You didn't specify the standard version. In standards prior to C++20, your code have underspecified behavior at best, in C++20 you have array `S[3]` in the allocated memory (even though the lifetime of elements of this array is not started). – Language Lawyer Feb 25 '21 at 10:42
  • @LanguageLawyer You mean, `new (data + 2) S()` is not great prior to C++20 as well, don't you? – yeputons Feb 25 '21 at 10:45
  • @rustyx allocation function shall allocate storage suitable for any non-extended alignment object https://timsong-cpp.github.io/cppwp/n4861/basic.stc.dynamic.allocation#3.3 – Language Lawyer Feb 25 '21 at 11:08
  • @LanguageLawyer Do you mean to say `std::string` is guaranteed to not use extended alignment? The provided example looks contrived, anyway. – rustyx Feb 25 '21 at 11:27
  • _You mean, new (data + 2) S() is not great prior to C++20 as well, don't you?_ Something like this. If you use an expression of type "pointer to `T`" in pointer arithmetic, it'd better to actually point to object of type [similar to] `T` (https://timsong-cpp.github.io/cppwp/n4659/expr.add#6) – Language Lawyer Feb 25 '21 at 12:34
  • @rustyx `new` guarantees that an allocation of `n` bytes is correctly aligned for an object of size `n` or larger. – Martin York Feb 25 '21 at 12:56
  • @MartinYork That's unless the object uses `alignas` internally. See [basic.stc.dynamic.allocation/3.3](https://timsong-cpp.github.io/cppwp/n4861/basic.stc.dynamic.allocation#3.3) – rustyx Feb 25 '21 at 13:02
  • @rustyx See: `n4868` Section: `7.6.2.8 New` paragraph 15. – Martin York Feb 25 '21 at 13:06
  • @rustyx Does `S`? `A new-extended alignment is represented by an alignment greater than __STDCPP_DEFAULT_NEW_ALIGNMENT__`'? – Martin York Feb 25 '21 at 13:10
  • @MartinYork Probably not, but as I said in another comment earlier, the example looks contrived, and in real life `S` just might. – rustyx Feb 25 '21 at 13:17

1 Answers1

4

If the operators in question have not been overloaded to mean something different, x[y] is equivalent to *(x+y). Somewhat amusingly also y[x] due to + being commutative.

In your example, &data[1] is an exact equivalent of data+1 and equally well-defined. UB would be reading the uninitialized memory, taking its address is not.

  • That leaves the question whether `&*x` is valid whenever `x` is a valid pointer, but does not point to a valid object. Looks like [it is](https://stackoverflow.com/questions/37724668/is-taking-uninitialized-references-of-class-members-during-construction-legal) – yeputons Feb 25 '21 at 10:04
  • 1
    @yeputons What is _valid object_? – Language Lawyer Feb 25 '21 at 10:42
  • @LanguageLawyer No idea, actually. I'm not sure what formalism goes here. – yeputons Feb 25 '21 at 10:46
  • 1
    @LanguageLawyer I take that to mean an object whose lifetime has been correctly started but not ended. – Martin York Feb 25 '21 at 13:12