4

As we know, std::vector when initialized like std::vector vect(n) or empty_vect.resize(n) not only allocates required amount of memory but also initializes it with default value (i.e. calls default constructor). This leads to unnecessary initialization especially if I have an array of integers and I'd like to fill it with some specific values that cannot be provided via any vector constructor.

Capacity on the other hand allocates the memory in call like empty_vect.reserve(n), but in this case vector still is empty. So size() returns 0, empty() returns true, operator[] generates exceptions.

Now, please look into the code:

{ // My scope starts here...

    std::vector<int> vect;
    vect.reserve(n);
    int *data = vect.data();

    // Here I know the size 'n' and I also have data pointer so I can use it as a regular array.
    // ...

} // Here ends my scope, so vector is destroyed, memory is released.

The question is if "so I can use it as array" is a safe assumption?

No matter for arguments, I am just curious of above question. Anyway, as for arguments:

  1. It allocates memory and automatically frees it on any return from function
  2. Code does not performs unnecessary data initialization (which may affect performance in some cases)
no one special
  • 1,608
  • 13
  • 32
  • 3
    So, you want to use it like `unique_ptr`. Then use `unique_ptr` if you don't need the vector API. – 273K Dec 20 '19 at 08:06
  • 1
    There is no objects in `data()`, you cannot use that pointer like that. – Jarod42 Dec 20 '19 at 08:07
  • 1
    There is no need to use `resize`, there are `insert`, `push_back` etc. – n. m. could be an AI Dec 20 '19 at 09:05
  • The accepted answer is correct, and gives a better why to do what the OP wants to do in C++20. Still, I believe the case is more subtle than many would think, and there is in fact a workaround even in C++03, see https://stackoverflow.com/a/69141237/2791230. – wpzdm Sep 21 '21 at 05:15

1 Answers1

10

No, you cannot use it.

The standard (current draft, equivalent wording in C++11) says in [vector.data]:

constexpr T* data() noexcept;

constexpr const T* data() const noexcept;

Returns: A pointer such that [data(), data() + size()) is a valid range. For a non-empty vector, data() == addressof(front()).

You don't have any guarantee that you can access through the pointer beyond the vector's size. In particular, for an empty vector, the last sentence doesn't apply and so you cannot even be sure that you are getting a valid pointer to the underlying array.

There is currently no way to use std::vector with default-initialized elements.


As mentioned in the comments, you can use std::unique_ptr instead (requires #inclue<memory>):

auto data = std::unique_ptr<int[]>{new int[n]};

which will give you a std::unique_ptr<int[]> smart pointer to a dynamically sized array of int's, which will be destroyed automatically when the lifetime of data ends and that can transfer it's ownership via move operations.

It can be dereferenced and indexed directly with the usual pointer syntax, but does not allow direct pointer arithmetic. A raw pointer can be obtained from it via data.get().

It does not offer you the std::vector interface, though. In particular it does not provide access to its allocation size and cannot be copied.


Note: I made a mistake in a previous version of this answer. I used std::make_unique<int[]> without realizing that it actually also performs value-initialization (initialize to zero for ints). In C++20 there will be std::make_unique_default_init<int[]> which will default-initialize (and therefore leave ints with indeterminate value).

walnut
  • 21,629
  • 4
  • 23
  • 59
  • if OP did a pushback(0) after the reserve then data() would return a valid pointer. Does this make his assumptions safe(r) ? – engf-010 Feb 29 '20 at 02:28
  • @engf-010 Yes, the pointer would be valid, but it doesn't really help for what OP wants to do. The cited clause only says that `[data(), data() + size())` is a valid range. Therefore the most you are allowed to do is add `size()` to `data()` (which gives you a pointer past the end of the range that may not be dereferenced). There is no guarantee that even pointer arithmetic beyond that is valid. – walnut Feb 29 '20 at 02:45
  • 1
    from your reply I take that you (and me also) are not totally convinced about this even though your formally correct. But reading about all reallocation clauses in vector I can only conclude that that range restriction is overly strict. – engf-010 Feb 29 '20 at 02:52
  • @engf-010 Pointer arithmetic in general has undefined behavior if you go outside the array to which the pointed-to object belongs. `std::vector` guarantees you that the initialized elements form an array on which pointer arithmetic works, but there are no constructed `int` objects outside the `[data(), data() + size)` range, so pointer arithmetic beyond what I mentioned will have undefined behavior. – walnut Feb 29 '20 at 02:55