I have a class called 'Container' in which three (known at compile time) objects of class DontCopyMe must be stored. Class DontCopyMe has non-default constructor and deleted copy constructors. How can I initialize Containter?
Sample code:
#include <string>
class DontCopyMe
{
public:
DontCopyMe(const unsigned int SomeInt, const std::string & SomeString): SomeInt(SomeInt), SomeString(SomeString)
{
}
DontCopyMe(const DontCopyMe &) = delete;
DontCopyMe & operator = (const DontCopyMe &) = delete;
DontCopyMe(DontCopyMe &&) = delete;
DontCopyMe & operator = (DontCopyMe &&) = delete;
private:
const unsigned int SomeInt;
const std::string SomeString;
};
class Container
{
public:
Container(): Array{{1, "A"}, {2, "B"}, {3, "C"}}
{
}
private:
DontCopyMe Array[3];
};
int main()
{
Container C;
return 0;
}
Of course I'm getting:
main.cpp: In constructor 'Container::Container()':
main.cpp:22:56: error: use of deleted function 'DontCopyMe::DontCopyMe(DontCopyMe&&)'
Container(): Array{{1, "A"}, {2, "B"}, {3, "C"}}
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.
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