How to get array size stored in unique_ptr?
Asked Answered
M

5

22

if I do:

std::unique_ptr<int[]> uparr(new int[1984]);

and I pass uparr to somebody without passing the 1984 to them can they see how many elements it has?
aka is there equivalent of vector's .size() for unique_ptr of array ?

Moralize answered 26/3, 2014 at 13:21 Comment(0)
C
22

No, there isn't. Dynamic arrays are a somewhat defective language feature. You'll essentially always want/need to pass the array length around separately (unless you have some kind of sentinel policy). So you might as well use std::vector (if you don't mind the extra word for the capacity).

Chaplet answered 26/3, 2014 at 13:23 Comment(6)
it is actually 2 extra words, one for size, one for capacity :)Moralize
@NoSenseEtAl: As just discussed, the size is what you want anyway. Only the capacity is superfluous to a one-off fixed size dynamic array.Chaplet
It is actually implementation dependent, even if most implementation is three pointers in the case of a stateless allocator, there is no guarantee.Adamek
If you use the std::vector you cannot pass the buffer to functions that operate on a byte array - or is there a way?Lusatian
@FruitBreak: Why not? Vector storage is guaranteed to be contiguous.Chaplet
@FruitBreak the data member function gives you a pointer to the elementsRocher
A
5

No, the information is lost.

If you need to keep track of it, use a vector instead or if you really do not wants the extra services, write a small wrapper around an allocated array. You can for example look at the std::dynarray proposal that was kicked off the c++1y standard.

Adamek answered 26/3, 2014 at 13:22 Comment(0)
O
1

unique_ptr does not carry the size information. You could use vector, but there are two issues:

  1. The size is not fixed. That is sometimes an important property.
  2. Forces a value-initialization onto each element, even if you don't want to.

You can wrap unique_ptr with a size and make a custom container:

#include <cstddef>  // for size_t
#include <cstdio>   // for printf
#include <memory>   // for unique_ptr
#include <utility>  // for forward

template <class T>
struct DynArray final {
 private:
  std::unique_ptr<T[]> _data = nullptr;
  std::size_t _size = 0;

 public:
  static DynArray ofSize(std::size_t const size) {
    DynArray arr;

    arr._data.reset(size ? new T[size] : nullptr);
    arr._size = size;

    return arr;
  }

  template <class... Args>
  DynArray(Args&&... args)
      : _data(sizeof...(Args)
                  ? new T[sizeof...(Args)]{std::forward<Args>(args)...}
                  : nullptr),
        _size{sizeof...(Args)} {}

  DynArray(DynArray&& goner) noexcept
      : _data{std::move(goner._data)}, _size(goner._size) {
    goner._size = 0;
  }

  auto& operator=(DynArray&& goner) noexcept {
    if (this != &goner) {
      _data = std::move(goner._data);
      this->_size = goner._size;
      goner._size = 0;
    }

    return *this;
  }

  auto size() const noexcept { return _size; }
  auto const* begin() const noexcept { return _data.get(); }
  auto const* cbegin() const noexcept { return _data.get(); }
  auto* begin() noexcept { return _data.get(); }
  auto const* end() const noexcept { return begin() + _size; }
  auto const* cend() const noexcept { return begin() + _size; }
  auto* end() noexcept { return begin() + _size; }

  auto const* data() const noexcept { return begin(); }
  auto data() noexcept { return begin(); }

  auto const& operator[](std::size_t i) const noexcept { return _data[i]; }
  auto& operator[](std::size_t i) noexcept { return _data[i]; }
};

// Example usage

int main() {
  auto arr = DynArray<int>{1, 2, 3, 4, 5};

  for (auto elm : arr) printf("%d\n", elm);

  auto arr2 = DynArray<char>::ofSize(3);

  arr2[0] = 'A';
  arr2[1] = 'B';
  arr2[2] = 'C';

  for (auto elm : arr2) printf("%c\n", elm);
}

See online

Orji answered 9/11, 2021 at 11:48 Comment(0)
P
1

absl::fixed_array<> is a somewhat-recent solution, which also has the benefit (over unique_ptr<T[]>) that it may inline small arrays instead of allocating them on the heap.

https://github.com/abseil/abseil-cpp/blob/master/absl/container/fixed_array.h

Pertinacity answered 8/9, 2023 at 11:25 Comment(0)
S
0

You pass this information along with the smart pointer, as an extra parameter, or within a std::tuple or std::pair. This is necessary because of c++ zero overhead principle.

Sergent answered 9/11, 2021 at 11:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.