I tried to write an "inline-vector" class for storing a number of elements on the stack conveniently:
template<typename T, size_t size_ = 256>
struct InlineVector{
T content[size_];
size_t num;
T() : num(0) {}
~T() { for(size_t s = 0; s < num; s++){ content[s]->~T(); } }
template<typename _Up, typename... _Args>
void emplace_back(_Args&&... __args) { new (&content[num++]) T(__args); }
T& get(size_t i) { assert(i < num); return content[i]; }
}
For efficiency reasons, I want that content is not initialized in X's constructor, even if T has only a non-trivial constructor. As you see, the content is intialized later with placement new when it is actually inserted. However, C++ seems to enforce that all elements of content must be initialized in the initialization of X. For example, this does not compile:
struct Y{ Y(int){} }
X<Y> foo; // compile error, no constructor for Y is called in the construction of X
So, how is it possible to have an array member that is not initialized, even if the array element type needs a constructor?
By making the array an array of chars and later placement new-ing into it. char is the only type you're allowed to do this with.
Whilst you're at it, then, you might as well write your own allocator instead (with the array as one of its members), and plug it into std::vector rather than re-inventing the whole shebang.
std::vector<int, YourAllocatorType<int, 256>> v;
So, how is it possible to have an array member that is not initialized, even if the array element type needs a constructor?
It's not for T x[n]... but you can point any old T* at properly aligned uninitialised memory, then manage your own ad-hoc construction and destruction. You could also explore creating a union with your type and say a char, and have an array of the unions....
Related
I am attempting to learn C++ and programming and thus I'm implementing some of the data structures in the STL.
As for my version of vector, I have two constructors, where one of them allows you to specify a location in memory for storage, if you so please. If not I have set the address of the internal data to nullptr.
I'm wondering how this will behave. Using the first constructor, I assume m_data will be set to nullptr, but what happens in the second case? Is m_data first set to nullptr, and then overwritten in construction? Is there a reason to keep the definition as nullptr in the definition, or should I just use the initializer list in both cases?
Here is the code:
template <typename T, size_t S = 0>
class custom::vector {
private:
T* m_data = nullptr;
size_t m_size;
size_t m_capacity;
T* m_future_data = nullptr;
size_t m_future_cap;
public:
// Constructors
vector() : m_size(S), m_capacity(S) {}
vector(T* destination, const size_t& capacity) : m_size(S) {
m_data = destination;
m_capacity = capacity;
}
// .... rest of class
It's tricky so see in your specific case, because T* is a pointer, and pointers do not have constructors. If you have a member with class type, you can see which constructor is called.
What you'll then see is that members are initialized (ctor, not assignment) once. The initializer used will be the one in the initializer list, if one is available there, or from the member definition if there's no initializer in the initializer list.
As for the question whether you should keep the default for m_data, I'd say no. The main reason for that is that you should have a proper class invariant, and I suspect that invariant should link m_data and m_size. But m_size has no default in its definition - it could be S, perhaps. But you should be consistent there.
Since C++14 (it might be C++11, i'm not sure), zero initialization happens on class construction on certain conditions, depending on the way the constructor is called.
Is there a way to ensure a raw value field (let's say a pointer) is never zero initialized ?
I guess not because it seems that zero initialization happens at the class level and not at the field level (surely a kind of memset(this, 0, sizeof(TheClass))), but I'm still hoping there is way, a hack, something...
The idea is to be able to initialize a field before a placement new is called so that that member is available during construction time.
According to cppreference's take on zero initialization:
If T is an non-union class type, all base classes and non-static data
members are zero-initialized, and all padding is initialized to zero
bits. The constructors, if any, are ignored.
If your object is a victim of zero initialization you're out of luck. And every object with static or thread local storage duration will always be zero initialized (except for constant initialization).
Is there a way to ensure a raw value field (let's say a pointer) is never zero initialized ?
It is possible, yes.
If the class is trivially default constructible, then simply default initialise the object, and its members will be default initialised as well:
int main() {
T t; // default initialised
If the class is not trivially default constructible, you can write a user defined constructor that leaves the member uninitialised (note that doing this is generally discouraged):
struct S {
int member;
S(){} // member is left default initialised
Warning: With a user defined constructor, the member would be left default initialised even if the object is value initialised.
Objects with static storage duration are always zero or constant initialised. Neither of those leave members uninitialised. Only way to avoid that initialisation is to not create objects with static storage duration.
The idea is to be able to initialize a field before a placement new is called so that that member is available during construction time.
If you mean your idea is to initialise a field of an object that hasn't yet been created with placement new, then the idea is not possible to implement.
Members are available during construction time. You don't need any tricks to achieve this. All members that can be used by their siblings in order of their initialisation. And the constructor body is run after all members have been initialised.
As #eerorika's answer suggests:
struct S {
int member;
S(){} // member is left default initialised
...note that doing this is generally discouraged
I would say that the clearest way to achieve this without confusing the user of the class would be not to use a int, but a std::aligned_storage_t as it is clear that this member is used just for storage of objects that might be created later rather than a value that can be used on its own.
Depending how much code you would like to write, this might be tedious, because you have to write placement news and use std::launder (since c++17) to be compliant with the standard. However, this is IMO the best way to express your intent.
cppreference gives a good example of how to use std::aligned_storage (with some modification, see comments):
#include <iostream>
#include <type_traits>
#include <string>
template<class T, std::size_t N>
class static_vector
{
// properly aligned uninitialized storage for N T's
typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
std::size_t m_size = 0;
public:
// My modification here suppresses zero-initialization
// if initialized with empty braces
static_vector() {};
// Create an object in aligned storage
template<typename ...Args> void emplace_back(Args&&... args)
{
if( m_size >= N ) // possible error handling
throw std::bad_alloc{};
// construct value in memory of aligned storage
// using inplace operator new
new(&data[m_size]) T(std::forward<Args>(args)...);
++m_size;
}
// Access an object in aligned storage
const T& operator[](std::size_t pos) const
{
// note: needs std::launder as of C++17
return *reinterpret_cast<const T*>(&data[pos]);
}
// Delete objects from aligned storage
~static_vector()
{
for(std::size_t pos = 0; pos < m_size; ++pos) {
// note: needs std::launder as of C++17
reinterpret_cast<T*>(&data[pos])->~T();
}
}
};
int main()
{
static_vector<std::string, 10> v1;
v1.emplace_back(5, '*');
v1.emplace_back(10, '*');
std::cout << v1[0] << '\n' << v1[1] << '\n';
static_vector<std::size_t, 10> v2{};
// This is undefined behavior.
// Here it's just used to demonstrate that
// the memory is not initialized.
std::cout << v2[0] << "\n";
}
Run it on compiler explorer
However, since the c++ standard only mandates when zero-initialization must happen, but not when it must not happen, sometimes you really have to wrestle with your compiler in order to suppress zero initialization.
See this question and this question for more details.
When trying to build an array with a template, I get an error when
implementing a default constructor for the Array class template.
int main()
{
Array<int,5> arrayOfFiveInts;
return 0;
}
template<typename T, size_t SIZE>
class Array
{
public:
Array<T,SIZE>::Array()
{
elements = new T [SIZE];
for (int i = 0; i < SIZE; i++)
{
elements[i] = 0;
}
}
private:
T elements[SIZE];
};
I am expecting to see the array created when Main runs.
You have to decide whether you want to have the array in automatic or dynamic memory.
If you meant to have it in automatic memory, you have to remove the call to new[] in the constructor since elements is already allocated when it is declared.
If you meant to have it in dynamic memory, you have to change the declaration of elements to
T* elements;
If you do so, then you also need to make sure your class follows the Rule of 3/5/0. Add a destructor:
~Array()
{
delete[] elements;
}
As well as implement (or delete) a copy/move constructor, and a copy/move assignment operator.
templates are header-only, you don't need to use the extra qualification:
Array<T,SIZE>::Array()
You have to define the class before the main() function in your example.
Check the correct format in
this Live demo.
Just remove this line
elements = new T [SIZE];
elements is an array not a pointer, so trying to allocate some memory and assign to it doesn't make any sense.
This is probably a simple question but I have this template class:
template<typename Type>
class Array {
size_t n;
Type* buff;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
The code is from a course pdf file where it says buff(new Type[n]) is unsafe. I don't understand why it's unsafe, isn't size_t generally unsigned? Can I have an example where it could have a compile and/or run-time error?
The code is "unsafe" in the fact that it relies on n being constructed before buff. This dependency adds brittleness to the code.
When you construct the members of the class they are constructed in the order the are declared in the class, not how they are called in the member initialization list, so if the code was changed to
template<typename Type>
class Array {
Type* buff;
size_t n;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
Then when you do buff(new Type[n]), n is uninitialized and you have undefined behavior.
First of all the order, the initializations for the constructor are executed is not determined by the order they are written down, but by the order the initialized fields appear in the code:
class Array {
size_t n;
Type* buff;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
Here first n will be initialized and then buff.
class Array {
Type* buff;
size_t n;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
Now first buff will be initialized and then n, so n has no defined value in that case.
Using initialization lists for constructors is good practice, but be careful, that you don't create any assumptions on the order.
Generally it is a good idea to refrain from owning raw pointers. If you use smart pointers instead, you can not forget to release the data.
In the specific case, you might also want to use std::vector instead of a C-style array. That handles all the allocation, reallocation, releases, etc. in a thread safe manner for you. It seems like you are trying to write something like your own std::vector. Please only do that for educational purposes. Always prefer the standard implementation in production code. You probably won't get it better for quite a while. If you did, you would ask different questions here ;-)
First of all, you have a memory leak. But the question probably isn't about that. So let's assume you have a destructor that deallocates the array.
template<typename Type>
class Array {
size_t n;
Type* buff;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
~Array() { delete[] buff; }
};
Now this particular code is perfectly safe. No exception can be thrown while assigning n_, the order of initialization is correct and buff is the only raw pointer in your class. However, as you start expanding your class and writing more classes, the risk of a memory leak increases.
Imagine that you need to add one more members to the class Array:
template<typename Type>
class Array {
size_t n;
Type* buff;
SomethingElse xyz;
public:
Array(size_t n_): n(n_), buff(new Type[n_]), xyz(n_) {}
~Array() { delete[] buff; }
};
If the constructor of SomethingElse throws, the memory allocated for buff will leak, because the destructor ~Array() will never be called.
Modern C++ calls pointers such as Type* buff raw pointers because you are responsible for deallocating storage yourself (taking exceptions into account), and introduces tools such as std::unique_ptr and std::shared_ptr that can take care of storage deallocation automatically.
In modern C++ you could write your class like this:
template<typename Type>
class Array {
size_t n;
std::unique_ptr<Type[]> buff;
public:
Array(size_t n_): n(n_), buff(new Type[n_]) {}
};
Notice the absence of a destructor. The unique_ptr will take care of calling delete for you.
Note also no dependency on class members inside the initializer list (simply writing new Type[n_] instead of new Type[n] makes your code more robust)
C++98 Standard 12.6.2.5.4 (I don't expect new versions to have relaxed this).
— Then, nonstatic data members shall be initialized in the order they
were declared in the class definition (again regardless of the order
of the mem-initializers).
So the order of initialization is defined according to this.
If you want an example of how to crash it simply make sizeof(Type)*n > total memory in your system.
It is not safe to call new operator inside initialization list.
if new fails the destructor of Array will not be called.
here is a similar question.
Are there any issues with allocating memory within constructor initialization lists?
Suppose I have a class...
class Foo
{
public:
Foo(int size);
private:
const int size;
int data[];
};
Supposing that the size field is set immediately at instantiation, how can I set the length of data based on that size input?
I would ordinarily use a std::vector here, but I am writing a library for Arduino, so that won't fly, and I'm trying to avoid external dependencies if I can.
You are out of luck here, as C++ must know the size of the array at compile time (in other words, the size must be a constant expression). Your options are to either use dynamic allocation
int* data;
then allocate with new int[size]; in the constructor initialization list, or, better, use std::unique_ptr<> (C++11 or later), which is a light wrapper around a raw pointer and deletes its allocated memory at scope exit, so you won't have to manually delete[]
class Foo
{
private:
std::unique_ptr<int[]> data; // no need to manually delete
public:
Foo(int size): data{new int[size]} {}
};
A third option is to make Foo a non-type template class (assuming you know the size at compile time, which is what it seems to be happening, at least judging from your question)
template<std::size_t size>
class Foo
{
int data[size];
public:
Foo()
{
// constructor here
}
};