Reputation: 493
I've wanted a wrapper around arrays, such, it would be stored at the stack - to be not concerned about memory releasing - be initializable via brace lists, and possibly be substitutable in any place of an ordinary array. Then, I've produced the following code. And now am wondering, have I missed something. -- So - is it what I've wanted?
template<class T, size_t size>
struct Array
{
T body[size];
operator T* () { return body; }
};
Edit:
I might be imprecise. The wrapper is only for constructional purpose. It shall be used for constructing arrays from brace lists, when being in an initialization list (primarily). Like
class A {
protected: A(int array[])
...
class B : public A {
public: B() :
A( (Array<int, 2>) {{ 1, 2 }} )
...
There was a proposition of a const
version of the casting operator. - I've been considering this, but am not sure, is it really needed. While casting to const T[]
is done implicitly through the existing operator, and a constant array can be defined by giving T = const ...
, is there still a reason?
Upvotes: 1
Views: 4707
Reputation: 96810
For a basic example, I don't think there's much you can improve on, except for a few helper functions. In particular, it would be nice to have a method that returns the size:
constexpr std::size_t size() const { return size; }
In addition, here are a few others:
const
/non-const
overloads of operator[N]
:
As @ChristianRau stated in the comments, a operator T*
provides a non-const
version. We can implement the const
version as such:
T const& operator [](std::size_t n) const
{
return body[n];
}
// similarly for non-const:
T& operator [](std::size_t n) { return body[n]; }
begin()
and end()
sequencers (very useful e.g. for the C++11 range-based for):
T* begin() { return body; }
T* end() { return body + size; }
// const versions... very much the same
T const* cbegin() const { return body; }
T const* cend() const { return body + size; }
T const* begin() const { return cbegin(); }
T const* end() const { return cend(); }
an at()
method, which includes bounds checking (as opposed to operator[]
by convention):
T const& at(std::size_t offset) const
{
// You should do bounds checking here
return body[offset];
}
// also a non-const version again..
It would also be nice to have a constructor that takes an std::initializer_list<T>
so that you don't have to use the aggregate-initialization:
#include <algorithm>
#include <initializer_list>
template <typename T, std::size_t N>
struct Array
{
...
Array(std::initializer_list<T> const& list)
{
std::copy(list.begin(), list.end(), body);
}
...
};
Here is another one suggested by @DyP (initializer list always copies, perfect forwarding tries to avoid that):
template <typename T, std::size_t N>
struct Array
{
...
template <typename... Args>
// possibly constexpr
Array(Args&&... args) : body{ std::forward<Args>(args)... }
{}
...
};
Here is the program in action if you want to see it -- http://ideone.com/Zs27it#view_edit_box
There are others features you can include, but as I said this is a basic example, and you would most likely be better off using something like std::array
which has more or less the same methods.
Upvotes: 6