How do I declare an array of objects whose class has no default constructor?
For an array you would have to provide an initializer for each element of the array at the point where you define the array.
For a vector you can provide an instance to copy for each member of the vector.
e.g.
std::vector<Foo> thousand_foos(1000, Foo(42));
intializing a std::array of objects that don't have default constructor from another constexpr std::array
With std::index_sequence
:
template <typename T, typename U, std::size_t N, std::size_t ... Is>
constexpr std::array<T, N> make_array(const std::array<U, N>& a, std::index_sequence<Is...>)
{
return {{T(a[Is])...}};
}
template <typename T, typename U, std::size_t N>
constexpr std::array<T, N> make_array(const std::array<U, N>& a)
{
return make_array<T>(a, std::make_index_sequence<N>());
}
Usage:
static constexpr std::array<int, 4> arr = {1,6,0,4};
/*constexpr*/ std::array<X, arr.size()> xArr = make_array<X>(arr);
Demo
Class member array of objects without default constructor and deleted copy constructors initialization
clang++ 3.6.2
compiles your code. g++ 5.2.0
does not.
According to this report, this is a bug in g++.
Here's a (nasty) workaround, for the moment: use std::aligned_storage
, placement new
and explicit destructor calls:
#include <type_traits>
class Container
{
private:
using Storage = typename std::aligned_storage
<
sizeof(DontCopyMe),
alignof(DontCopyMe)
>::type;
public:
Container()
{
// Construct an instance of `DontCopyMe` in the memory
// location starting at `&Array[0]`.
new (&Array[0]) DontCopyMe{1, "A"};
// ...
new (&Array[1]) DontCopyMe{2, "B"};
new (&Array[2]) DontCopyMe{3, "C"};
// You can also (and should) use a for-loop.
}
~Container()
{
// Interpret the bytes at location `&Array[2]` as if
// they were a `DontCopyMe` instance, then call the
// `~DontCopyMe()` destructor on it.
(reinterpret_cast<DontCopyMe*>(&Array[2]))->~DontCopyMe();
// ...
(reinterpret_cast<DontCopyMe*>(&Array[1]))->~DontCopyMe();
(reinterpret_cast<DontCopyMe*>(&Array[0]))->~DontCopyMe();
// You can also (and should) use a for-loop.
}
private:
Storage Array[3];
};
You'll have to implement move and copy operations for Containers
that cleanup the aligned storage properly. You may want to wrap all aligned_storage
-related code in an helper class.
You mentioned you require additional safety and constness. The best way to achieve this is wrapping std::aligned_storage
in an helper class that will make sure you don't make mistakes.
Here's some code to get you started:
#include <type_traits>
#include <utility>
#include <cassert>
template<typename T, std::size_t TSize>
struct ASImmutableArray
{
private:
using ThisType = ASImmutableArray<T, TSize>;
using Storage = typename std::aligned_storage
<
sizeof(T),
alignof(T)
>::type;
Storage data[TSize];
template<typename... Ts>
void initAt(std::size_t mIndex, Ts&&... mXs)
{
assert(mIndex >= 0 && mIndex < TSize);
// assert the data was not initialized
new (&data[mIndex]) T(std::forward<Ts>(mXs)...);
}
void deinitAt(std::size_t mIndex)
{
assert(mIndex >= 0 && mIndex < TSize);
// assert the data was actually initialized
reinterpret_cast<T*>(&data[mIndex])->~T();
}
public:
// ...
};
An idea is passing std::tuple
instances in the constructor of ASImmutableArray
, and creating T
instances in place using placement new
at the correct indices by expanding the tuples and forwarding their contents to T
's constructor. The tuples would contain the same types as the types required to construct T
.
You can also keep track of initialized/deinitialized items with an additional member boolean array (that can be disabled in release builds, and only used for verifying the correct usage of the class during development).
If you want an example of an (old) implementation of something similar, this is something I've written for one of my libraries.
You can also check out this tagged union implementation I've written to see an example on how I use debug-only member variables that have no overhead in release-builds for additional safety.
Object array initialization without default constructor
Nope.
But lo! If you use std::vector<Car>
, like you should be (never ever use new[]
), then you can specify exactly how elements should be constructed*.
*Well sort of. You can specify the value of which to make copies of.
Like this:
#include <iostream>
#include <vector>
class Car
{
private:
Car(); // if you don't use it, you can just declare it to make it private
int _no;
public:
Car(int no) :
_no(no)
{
// use an initialization list to initialize members,
// not the constructor body to assign them
}
void printNo()
{
// use whitespace, itmakesthingseasiertoread
std::cout << _no << std::endl;
}
};
int main()
{
int userInput = 10;
// first method: userInput copies of Car(5)
std::vector<Car> mycars(userInput, Car(5));
// second method:
std::vector<Car> mycars; // empty
mycars.reserve(userInput); // optional: reserve the memory upfront
for (int i = 0; i < userInput; ++i)
mycars.push_back(Car(i)); // ith element is a copy of this
// return 0 is implicit on main's with no return statement,
// useful for snippets and short code samples
}
With the additional function:
void printCarNumbers(Car *cars, int length)
{
for(int i = 0; i < length; i++) // whitespace! :)
std::cout << cars[i].printNo();
}
int main()
{
// ...
printCarNumbers(&mycars[0], mycars.size());
}
Note printCarNumbers
really should be designed differently, to accept two iterators denoting a range.
How to initialize array of classes with deleted copy constructor (C++11)
I agree with the comments that this seems to be a GCC bug (reported as 63707).
It only fails to compile when the type in the array has a user-defined destructor, which doesn't make sense to me.
How can I use unique pointers for an array of parameterized objects in C++ using Visual Studio 2017?
g++ 9.2.0 tells me that you lack default constructor, i.e. one without parameters. Adding such constructor works fine. If it's not what you want, you can create array of unique_ptr's, so std::unique_ptr<std::unique_ptr<Test>[]>
and after that initialize each element by hand, something similar to this:
#include <memory>
#include <algorithm>
#include <iostream>
struct Test {
std::string str_;
Test(std::string const& str) : str_(str) { }
void print() { std::cout << str_ << '\n'; }
};
int main()
{
std::unique_ptr<std::unique_ptr<Test>[]> m_Tests;
int testCount = 2;
std::string path1{"a"}, path2{"b"};
m_Tests = std::make_unique<std::unique_ptr<Test>[]>(testCount);
std::array<std::string, 2> paths{path1, path2};
std::transform(paths.begin(), paths.end(), &m_Tests[0],
[](auto const& p) { return std::make_unique<Test>(p); });
for (int i = 0 ; i < testCount ; ++i) {
m_Tests[i]->print();
}
}
Related Topics
How to Generate Random Numbers in C++
Training Custom Svm to Use with Hogdescriptor in Opencv
Is There an Automatic Noexcept Specifier
When Extending a Padded Struct, Why Can't Extra Fields Be Placed in the Tail Padding
Declaring and Initializing a Variable in a Conditional or Control Statement in C++
Why Put the Constant Before the Variable in a Comparison
What Does "#Define Str(A) #A" Do
C++ Warning: Address of Local Variable
Does 'Auto' Type Assignments of a Pointer in C++11 Require '*'
Why Does Wide File-Stream in C++ Narrow Written Data by Default
Structured Binding with [[Maybe_Unused]]
How to Select a Random Element in Std::Set
Arithmetic Right Shift Gives Bogus Result
Enable a Single Warning in Visual Studio
What Is C++ Version of Realloc(), to Allocate the New Buffer and Copy the Contents from the Old One
What Is the Vtable Layout and Vtable Pointer Location in C++ Objects in Gcc 3.X and 4.X