For built-in types, like int, you can delay the initialization simply writing nothing. Is there a way to do the same for C++ objects?
I wrote this code that does the job, but I was wondering if there is an idiomatic way. If so, what is it? Was it even possible before the introduction of aligned storage?
#include <utility>
#include <type_traits>
template <typename T>
struct delayed {
delayed() { unset_init(); }
template <typename...Args> void init(Args&&... args) {
new ( memory() ) T(std::forward<Args>(args)...);
set_init();
}
operator T*() {
return memory();
}
~delayed() {
if (get_init()) {
memory()->~T();
unset_init();
}
}
private:
T* memory() { return reinterpret_cast<T*>(&bytes_); }
unsigned char* raw_memory() { return reinterpret_cast<unsigned char*>(&bytes_); }
unsigned char& init() { return *( raw_memory() + sizeof(T) ); }
bool get_init() { return init() != 0; }
void set_init() { init() = 1; }
void unset_init() { init() = 0; }
typename std::aligned_storage<sizeof(T) + 1, alignof(T)>::type bytes_{};
};
In C++17 and later I expect the preferred idiom will be std::optional<T>
. In C++11 and C++14 it seems that std::unique_ptr<T>
is common though it has the obvious drawback of requiring a heap allocation.
Usage:
std::optional<T> t; // initially empty
// do some stuff
// now we're ready to create the T value
t.emplace(foo, bar); // constructs the T with foo, bar as args
First, an int
variable is a C++ object. Presumably when you talk about C++ objects as opposed to int
, you mean class type objects. But not just class type objects, because you can do this:
struct Blah{ int x; int y; };
auto main() -> int
{
Blah o; // Uninitialized, indeterminate value.
// Whatever
o = {6, 7};
};
So probably you mean object of a class type with at least one user-defined constructor.
The most common ways to delay initialization of such an object, relative to the declaration of something used to access it, include
std::vector
as an expanding array,… where the refactoring essentially is about moving later usage code to a function or functions.
For example, the ugly and inefficient delayed-initialization code
unique_ptr<MyClass> p;
if( condition() )
{
// Some code here, then
p.reset( new MyDerivedA( 123 ) );
}
else
{
// Some code here, then
p.reset( new MyDerivedB( "andromeda" ) );
}
// Code using *p here.
… might be refactored as
void foo( MyClass&& o )
{
// Code using o here.
}
…
if( condition() )
{
// Some code here, then
foo( MyDerivedA( 123 ) );
}
else
{
// Some code here, then
foo( MyDerivedB( "andromeda" ) );
}
Less common ways include
placement new
in some suitably aligned byte array, like in your code, and
if your class is movable, using an Optional_
class (Barton-Nackman Fallible
, Boost and C++17 optional
) that supports move assignment.
Whether these techniques can be regarded as idiomatic for the purpose of delaying initialization, is, I think, quite subjective, personal opinion.
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