Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I reserve space on the stack for a non-default constructible?

I would basically write the following piece of code. I understand why it can't compile.

A instance; // A is a non-default-constructable type and therefore can't be allocated like this

if (something)
{
    instance = A("foo"); // use a constructor X
}
else
{
    instance = A(42); // use *another* constructor Y
}

instance.do_something();

Is there a way to achieve this behaviour without involving heap-allocation?

like image 441
Jack Sabbath Avatar asked Mar 27 '15 12:03

Jack Sabbath


1 Answers

There are better, cleaner ways to solve the problem than explicitly reserving space on the stack, such as using a conditional expression.

However if the type is not move constructible, or you have more complicated conditions that mean you really do need to reserve space on the stack to construct something later in two different places, you can use the solution below.

The standard library provides the aligned_storage trait, such that aligned_storage<T>::type is a POD type of the right size and alignment for storing a T, so you can use that to reserve the space, then use placement-new to construct an object into that buffer:

std::aligned_storage<A>::type buf;
A* ptr;
if (cond)
{
  // ...
  ptr = ::new (&buf) A("foo");
}
else
{
  // ...
  ptr = ::new (&buf) A(42);
}
A& instance = *ptr;

Just remember to destroy it manually too, which you could do with a unique_ptr and custom deleter:

struct destroy_A {
  void operator()(A* a) const { a->~A(); }
};
std::unique_ptr<A, destroy_A> cleanup(ptr);

Or using a lambda, although this wastes an extra pointer on the stack ;-)

std::unique_ptr<A, void(*)(A*)> cleanup(ptr, [](A* a){ a->~A();});

Or even just a dedicated local type instead of using unique_ptr

struct Cleanup {
  A* a;
  ~Cleanup() { a->~A(); }
} cleanup = { ptr };
like image 136
Jonathan Wakely Avatar answered Oct 12 '22 15:10

Jonathan Wakely