What is the most idiomatic way to “delay” the construction of a C ++ object?

For built-in types like int, you can delay initialization, just don't write anything. Is there a way to do the same for C ++ objects?

I wrote this code that does this work, but I was wondering if there is an idiomatic way. If so, then what is it? Is this possible before the introduction of leveled 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_{}; }; 
+7
source share
4 answers

In C ++ 17 and later, I expect the preferred idiom to be std::optional<T> . In C ++ 11 and C ++ 14, it seems that std::unique_ptr<T> is common, although it has an obvious flaw in requiring heap allocation.

Using:

 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 
+17
source

First, the int variable is a C ++ object. Presumably, when you talk about C ++ objects, not 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 you probably mean an object of type class with at least one user-defined constructor.

The most common ways to delay the initialization of such an object with respect to declaring something used to access it include

  • std::vector as an expanding array,
  • direct dynamic distribution (regardless of how life time is managed) and
  • code refactoring

& Hellip; where refactoring, in essence, is to move the subsequent usage code into a function or functions.

For example, ugly and ineffective deferred 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. 

& hellip; can be changed 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 methods include

  • put new in some appropriately aligned byte array, as in your code, and

  • if your class is movable, use the Optional_ class (Barton-Nackman Fallible , Boost, and C ++ 17 optional ), which supports move assignment.

Whether these methods can be considered idiomatic in order to delay initialization, I think this is a very subjective personal opinion.

+4
source

I do not know about the idiomatic way of doing this. Other answers are really interesting ( std::optional should become part of the standard library, but can it be considered idiomatic?). Here is another example of delayed initialization based on another idiom, pimpl (minimal working example):

 #include <memory> #include <iostream> struct I { void doSomething(int i) { val = i; } int getSomeData() { return val; } int val; }; class C { static I& initialized(C &c) { std::cout << "is initialized" << std::endl; return *(c.impl); } static I& uninitialized(C &c) { std::cout << "was uninitialized" << std::endl; c.impl = std::make_unique<I>(); c.getter = &initialized; return c.getter(c); } public: C(): impl{nullptr}, getter{&uninitialized} {} void doSomething(int i) { getter(*this).doSomething(i); } int getSomeData() { return getter(*this).getSomeData(); } private: using Getter = I&(*)(C &); std::unique_ptr<I> impl; Getter getter; }; int main() { C c; c.doSomething(42); c.getSomeData(); } 

In this case, the class is nothing more than a wrapper for the totality of data and functions contained in another class.
By delaying the construction of the internal representation, you actually delay the initialization of the outer class.

0
source

If you can live with a potential copy or move instead of direct initialization, you can use union. I would prefer optional , either from std::experimental , or C ++ 17, or, for example, Nudges.

 #include <iostream> struct S { S(int, float) {std::cout << "S::S()" << std::endl;} ~S() {std::cout << "S::~S()" << std::endl;} }; template<typename T> union Delayed { bool initialized; T obj; Delayed(): initialized(false) {} ~Delayed() {} }; int main() { Delayed<S> d; std::cout << 1 <<std::endl; d.obj = S(1, 1.0); return 0; } 
0
source

All Articles