How does one suppress the automatic initialization and destruction of a type? While it is wonderful that T buffer[100]
automatically initializes all the elements of buffer
, and destroys them when they fall out of scope, this is not the behavior I want.
#include <iostream>
static int created = 0,
destroyed = 0;
struct S
{
S()
{
++created;
}
~S()
{
++destroyed;
}
};
template <typename T, size_t KCount>
class fixed_vector
{
private:
T m_buffer[KCount];
public:
fixed_vector()
{
// some way to suppress the automatic initialization of m_buffer
}
~fixed_vector()
{
// some way to suppress the automatic destruction of m_buffer
}
};
int main()
{
{
fixed_vector<S, 100> arr;
}
std::cout << "Created:\t" << created << std::endl;
std::cout << "Destroyed:\t" << destroyed << std::endl;
return 0;
}
The output of this program is:
Created: 100
Destroyed: 100
I would like it to be:
Created: 0
Destroyed: 0
My only idea is to make m_buffer
some trivially constructed and destructed type like char
and then rely on operator[]
to wrap the pointer math for me, although this seems like a horribly hacked solution. Another solution would be to use malloc
and free
, but that gives a level of indirection that I do not want.
The reason why I want this is because I am making a container and I do not want to pay for the initialization overhead of things that I will not use. For example, if my main
function was:
int main()
{
{
std::vector<S> vec;
vec.reserve(50);
}
std::cout << "Created:\t" << created << std::endl;
std::cout << "Destroyed:\t" << destroyed << std::endl;
return 0;
}
The output would be correct:
Created: 0
Destroyed: 0
You may want to look into boost::optional
template <typename> struct tovoid { typedef void type; };
template <typename T, size_t KCount, typename = void>
struct ArrayStorage {
typedef T type;
static T &get(T &t) { return t; }
};
template <typename T, size_t KCount>
struct ArrayStorage<T, KCount, typename tovoid<int T::*>::type> {
typedef boost::optional<T> type;
static T &get(boost::optional<T> &t) {
if(!t) t = boost::in_place();
return *t;
}
};
template <typename T, size_t KCount>
class Array
{
public:
T &operator[](std::ptrdiff_t i) {
return ArrayStorage<T, KCount>::get(m_buffer_[i]);
}
T const &operator[](std::ptrdiff_t i) const {
return ArrayStorage<T, KCount>::get(m_buffer_[i]);
}
mutable typename ArrayStorage<T, KCount>::type m_buffer_[KCount];
};
A specialization is done for class type that wraps them into an optional
, thus calling the constructor/destructor lazily. For non-class types, we don't need that wrapping. Not wrapping them means we can treat &a[0]
as a contiguous memory area and pass that address to C functions that want an array. boost::in_place
will create the class types in-place, without using a temporary T
or its copy constructor.
Not using inheritance or private members allow the class to stay an aggregate, allowing a convenient form of initialization
// only two strings are constructed
Array<std::string, 10> array = { a, b };
You can create the array as array of char
s and then use placement new
to create the elements when needed.
template <typename T, size_t KCount>
class Array
{
private:
char m_buffer[KCount*sizeof(T)]; // TODO make sure it's aligned correctly
T operator[](int i) {
return reinterpret_cast<T&>(m_buffer[i*sizeof(T)]);
}
After re-reading your question it seems that you want a sparse array, this sometimes goes by the name of map ;o) (of course the performance characteristics are different...)
template <typename T, size_t KCount>
class SparseArray {
std::map<size_t, T> m_map;
public:
T& operator[](size_t i) {
if (i > KCount)
throw "out of bounds";
return m_map[i];
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With