2

Can I be sure an std::vector (or, in general, any standard container) contains objects and not pointers to objects, no matter how complex the objects' class is, if it has constant size?

E.g.: in this simple case:

struct MyStruct { int a, b; };
std::vector<MyStruct> vs;

The resulting vector layout is:

[ ..., a1, b1, a2, b2, a3, b3, ... ]

Does the Standard guarantees the same happens in this (or more complex) case(s), where the size of the structure is supposed to be constant:

struct MyStruct2 { float f[10]; };
std::vector<MyStruct2> vs2;

With layout:

[ ..., f1[0], f1[1], ..., f1[9], f2[0], f2[1], ..., f2[9], ... ]

instead of:

[ ..., *pf1, *pf2, ... ]
pf1 = [ f1[0], f1[1], ..., f1[9] ]
pf2 = [ f2[0], f2[1], ..., f2[9] ]
Pietro
  • 12,086
  • 26
  • 100
  • 193
  • ... what's the real question here? The containers all let you obtain a reference to the object. Some hold on to them in a pre-defined layout, but why should that matter? – StoryTeller - Unslander Monica Oct 27 '21 at 11:06
  • 4
    Even in your simple `MyStruct` case you cannot necessarily assume that `a2` immediately follows `b1` in memory because the compiler is [free to add padding to your struct](https://stackoverflow.com/questions/5397447/struct-padding-in-c). The only assumption you can make is that element `[1]` is exactly one `sizeof(MyStruct)` offset from `[0]` for example. – Cory Kramer Oct 27 '21 at 11:08
  • 1
    `std::vector` holds `T`s, `std::vector holds `T*`s. – Evg Oct 27 '21 at 11:09
  • 1
    you can use type traits to do sfinae on it, maybe a create_vector function which only takes std::enable_if<!std::is_pointer... https://en.cppreference.com/w/cpp/types/is_pointer – Treebeard Oct 27 '21 at 11:10
  • 1
    There are two separate points here, (1) `std::vector` will store the value with the `value_type` you ask for, according to the template parameter you provide (if you provide a pointer type `T*`, it store pointer types) (2) If you want to have a vector which cannot hold a pointer type, you can make a thin wrapper around `std::vector` and use SINFAE to block pointer types or remove the pointer from the type. – Mansoor Oct 27 '21 at 11:13
  • Vector that contains pointers is a vector that contains objects, because pointers themselves are objects. – eerorika Oct 27 '21 at 11:17
  • The layouts are incorrect in both cases. First one should be `[ ..., MyStruct_1, MyStruct_2, MyStruct_3, ... ]` and the second one should be `[..., MyStruct2_1, MyStruct2_2, MyStruct2_3, ...]`. – Eljay Oct 27 '21 at 11:32
  • @Eljay - I just wrote how the objects' contents is placed in memory. – Pietro Oct 27 '21 at 12:19
  • I know what you wrote. What you wrote was incorrect. It is incorrect on the DS9K platform. It may be correct on your platform, but using that insider information to access or manipulate the data is **undefined behavior**. – Eljay Oct 27 '21 at 12:21
  • @Eljay - The purpose of my question is not to know how to have direct access to the vector's elements, but to know how they are laid out in memory for performance considerations. – Pietro Oct 27 '21 at 12:54
  • 1
    The objects are stored sequentially in memory. You only get pointers-to-objects if you have a vector of pointers, in which case those pointers would be stored sequentially in memory. (A pointer itself is an object.) – Eljay Oct 27 '21 at 13:12

1 Answers1

8

Since C++11 and onwards (C++03 nearly guarantees it), the data in a std::vector are contiguous with no gaps.

In particular if you have a pointer to an element in the std::vector, you can reach all other elements using pointer arithmetic.

Of course, pointer arithmetic works in sizeof units of your struct. And the struct itself may contain padding. The behaviour on attempting to reach a b, given a pointer to an a in your struct is undefined.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483