I am implementing a container in c++, a wrapper for an array in fact. I am not sure how to implement a constructor from initializer_list. I end up with this implementation but it seems to me really ugly. So could be an array allocated in a heap initialized by an initializer_list. Or is there an elegant way how to do it?
template <typename T> class sequence {
public:
sequence (size_t n): _data {new T[n]}, _size {n} {};
sequence (std::initializer_list<T> source);
~sequence() { delete[] _data; };
private:
pointer _data;
size_type _size;
};
//Initializer list constructor
template <class T> sequence<T>::sequence (std::initializer_list<T> source)
: sequence(source.size()) {
auto iterator = source.begin();
for ( int i=0; i < _size; i++) {
_data[i] = *iterator;
++iterator;
}
};
Depends on what you're trying to do.
If you're actually needing to use a sequence with bounded size, as determined at compile-time, then use std::array<T,std::size_t>, which is a wrapper around a C-style array (like what you are implementing), introduced in C++11.
However, as you said in one of your comments, you're doing this mostly for educational purposes. In that case, what you have is decent. The syntax could be cleaned up a little. Consider:
//Initializer list constructor
template <class T>
sequence<T>::sequence (std::initializer_list<T> source)
: sequence(source.size())
{
auto it = source.begin();
auto const e = source.cend();
auto d = data_;
while (it != e) {
*d++ = *it++;
}
};
That way you don't explicitly rely on the size(). You could consider making things more efficient by turning the it and e iterators into "move" iterators:
auto it = std::make_move_iterator(source.begin());
auto e = std::make_move_iterator(source.end());
That way, whenever it's dereferenced, its value is cast to an rvalue reference, allowing a move assignment.
Related
I'd like to be able to pass my custom container this std::span constructor:
template< class R >
explicit(extent != std::dynamic_extent)
constexpr span( R&& range );
What do I need to add to my custom class to make it satisfy the requirements to be able to pass it to the std::span constructor that receives a range?
For example, with std::vector we are able to do this:
std::vector<int> v = {1, 2, 3};
auto span = std::span{v};
I've already added these to my custom class:
Type* begin()
{
return m_Data;
}
Type* end()
{
return m_Data + m_Length;
}
const Type* data() const
{
return m_Data;
}
size_t size() const
{
return m_Length;
}
...which in turn allowed me to use range-based loops, and std::data(my_container) as well as std::size(my_container). What am I missing to make it so that I can also pass my container to the std::span constructor? Does it require to implement a more complex iterator?
This is because your Container does not satisfy contiguous_range, which is defined as:
template<class T>
concept contiguous_range =
random_access_range<T> && contiguous_iterator<iterator_t<T>> &&
requires(T& t) {
{ ranges::data(t) } -> same_as<add_pointer_t<range_reference_t<T>>>;
};
In the requires clause, the return type of ranges::data(t) is const Type*, which is different from add_pointer_t<range_reference_t<T>> that is Type*.
The workaround is adding a non-const data() to your Container which return Type*:
Type* data() { return m_Data; }
template<typename T>
struct raster {
std::vector<T> v;
template<typename U, typename = std::enable_if_t<sizeof(T) == sizeof(U)>>
raster(raster<U>&& other){
// What code goes here?
}
}
Suppose we have raster<uint32_t> r such that r.v.size() is in the millions, and a guarantee that all its elements are within int32_t's range. Is it possible for raster<int32_t>::raster(raster<uint32_t>&& other) to avoid copying the memory backing other.v?
Or should I just do something like *reinterpret_cast<raster<int32_t>*>(&r) instead of calling that constructor?
There is no legal way to do this in C++; you can only move buffers from a std::vector to another std::vector of the exact same type.
There are a variety of ways you can hack this. The most illegal and evil would be
std::vector<uint32_t> evil_steal_memory( std::vector<int32_t>&& in ) {
return reinterpret_cast< std::vector<uint32_t>&& >(in);
}
or something similar.
A less evil way would be to forget it is a std::vector at all.
template<class T>
struct buffer {
template<class A>
buffer( std::vector<T,A> vec ):
m_begin( vec.data() ),
m_end( m_begin + vec.size() )
{
m_state = std::unique_ptr<void, void(*)(void*)>(
new std::vector<T,A>( std::move(vec) ),
[](void* ptr){
delete static_cast<std::vector<T,A>*>(ptr);
}
);
}
buffer(buffer&&)=default;
buffer& operator=(buffer&&)=default;
~buffer() = default;
T* begin() const { return m_begin; }
T* end() const { return m_end; }
std::size_t size() const { return begin()==end(); }
bool empty() const { return size()==0; }
T* data() const { return m_begin; }
T& operator[](std::size_t i) const {
return data()[i];
}
explicit operator bool() const { return (bool)m_state; }
template<class U>
using is_buffer_compatible = std::integral_constant<bool,
sizeof(U)==sizeof(T)
&& alignof(U)==alignof(T)
&& !std::is_pointer<T>{}
>;
template<class U,
std::enable_if_t< is_buffer_compatible<U>{}, bool > = true
>
buffer reinterpret( buffer<U> o ) {
return {std::move(o.m_state), reinterpret_cast<T*>(o.m_begin()),reinterpret_cast<T*>(o.m_end())};
}
private:
buffer(std::unique_ptr<void, void(*)(void*)> state, T* b, T* e):
m_state(std::move(state)),
m_begin(begin),
m_end(end)
{}
std::unique_ptr<void, void(*)(void*)> m_state;
T* m_begin = 0;
T* m_end = 0;
};
live example: this type erases a buffer of T.
template<class T>
struct raster {
buffer<T> v;
template<typename U, typename = std::enable_if_t<sizeof(T) == sizeof(U)>>
raster(raster<U>&& other):
v( buffer<T>::reinterpret( std::move(other).v ) )
{}
};
note that my buffer has a memory allocation in it; compared to millions of elements that is cheap. It is also move-only.
The memory allocation can be eliminated through careful use of the small buffer optimization.
I'd leave it move-only (who wants to accidentally copy a million elements?) and maybe write
buffer clone() const;
which creates a new buffer with the same contents.
Note that instead of a const buffer<int> you should use a buffer<const int> under the above design. You can change that by duplicating the begin() const methods to have const and non-const versions.
This solution relies on your belief that reinterpreting a buffer of int32_ts as a buffer of uint32_ts (or vice versa) doesn't mess with anything. Your compiler may provide this guarantee, but the C++ standard does not.
The problem is that the implementation of the vector template itself may be specialised for the type. For some weird whacky reason we don't understand today, the top-level vector might need an extra member not provided in vector, so simply reinterpret casting will not work safely.
Another evil approach might be to look at the Allocator of your two vectors.
If this was
a custom allocator you had written and both were a derived class from vector
and you wrote an overload in the class for swap and =&&
within which you created wrapped tmp vectors to swap with
and detected that the Allocator was being called within those temporary objects constructors/destructors, and on the same thread
to release and reallocate the same sized array
Then, perhaps you could legitimately pass the memory buffer between them without resetting the content.
But that is a lot of fragile work!
With a std::array you can initialize it like this:
std::array<int,5> myArray = {1,2,3,4,5};
If I where trying to create my own array class how would I do something similar?
std::array rely on aggregate initialization (the C way of initializing struct), basically this is valid c++:
struct A {
int values[2];
size_t size;
};
A a = {{42, 44}, 2U}; // No constructor involved
// | |--- a.size
// |--- a.values
Now if you remove the size attribute, you get:
struct A {
int values[2];
};
A a = {{42, 44}}; // Valid!
But c++ gives you something called brace-elision we allow you to omit the inner brackets, so:
A a = {42, 44}; // Still valid!
Now, just makes A a template:
template <typename T, size_t N>
struct A {
T data[N];
};
A<int, 2> a = {{42, 44}};
Note that this does not makes use of initializer_list which are used for std::vector, std::list, and so on.
Also note that:
A<int, 2> a1{42, 44}; // Not valid prior to c++14
A<int, 2> a2{{42, 44}}; // Ok even for c++11
And note that clang will throw warning whatever the version you use by default.
A minimalist version of std::array could look like this:
template <typename T, size_t N>
struct my_array {
T _data[N];
T& operator[] (size_t i) { return _data[i]; }
const T& operator[] (size_t i) const { return _data[i]; }
constexpr size_t size () const { return N; }
T* begin () { return _data; }
const T* begin () const{ return _data; }
T* end () { return _data + size(); }
T* end () const { return _data + size(); }
};
Please note that the standard std::array has a lot more stuff than this (a lots of typedefs, other methods, a lots of overloads, ...), but this is a small base to get you started.
Also note that _data has to be public so that aggregate initialization can work (it does not work if you have private / protected members!).
Here is a simple implementation that supports initializing with a list of elements:
//template class 'array' takes 'T' (the type of the array) and 'size'
template<typename T, std::size_t size>
class array
{
public:
//This is the concept of initializer lists (wrapper around variadic arguments, that
//allows for easy access)
array(std::initializer_list<T> list)
{
//Loop through each value in 'list', and assign it to internal array
for (std::size_t i = 0; i < list.size(); ++i)
arr[i] = *(list.begin() + i); //list.begin() returns an iterator to the firsts
//element, which can be moved using +
//(deferenced to get the value)
}
//Overloads operator[] for easy access (no bounds checking!)
T operator[](std::size_t index)
{
return arr[index];
}
private:
T arr[size]; //internal array
};
You can then use it like so:
array<int, 2> a = { 2, 5 };
int b = a[0]; //'b' == '2'
As the title says, I wonder if this is a proper way to prevent deallocation in vector<T> a when moving vector<T> a tovector<T> b.
Vector header:
template<class T, class A = std::allocator<T>>
class vector
{
typedef typename A::size_type size_type;
A alloc;
T* start;
T* end;
public:
explicit vector(size_type n, const T& val = T(), const A& =A());
vector(vector&&);
~vector();
};
Constructor:
Note: allocate can throw std::bad_alloc.
template<class T, class A>
vector<T,A>::vector(size_type n, const T& val, const A& a)
: alloc(a)
{
start = alloc.allocate(n); //allocate memory for n elements.
end = start + n;
for(auto p = start; p!=end; p++)
alloc.construct(p, val); //construct val at allocated memory.
}
Move constructor:
Question: Is this a proper way to move vector v?
template<class T, class A>
vector<T,A>::vector(vector&& v)
:alloc(v.alloc) // copy the allocator in v.
{
start = std::move(v.start); // move the pointer previously returned by allocator.
end = std::move(v.end); // same boundary value.
v.start = v.end = nullptr; // nullptr to prevent deallocation when v is destroyed.
}
Destructor:
Question: According to cppreference, allocator::deallocate(p, n) deallocates memory from a pointer previously returned by allocator and n must be equal to the number of elements allocated for. What if this is not the case? My answer would be that allocator::deallocate does nothing if the pointer or the number of elements, n, is not equal to previous call to allocator::allocate(n). Is this true?
template<class T, class A>
vector<T, A>::~vector()
{
for(auto p = start; p!=end; p++)
alloc.destroy(p); //Destroy objects pointed to by alloc.
alloc.deallocate(start, end-start); //Deallocate memory.
}
Is this a proper way to move vector v?
Seems fine, but std::move is redundant with pointers. Also, you don't deallocate end, so you don't need to set it to null.
What if this is not the case? My answer would be that allocator::deallocate does nothing if the pointer or the number of elements, n, is not equal to previous call to allocator::allocate(n). Is this true?
No, the behaviour is undefined. You must pass the same n.
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?
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.