如何为非默认构造预留堆栈空间?

时间:2015-03-27 12:39:01

标签: c++ allocation default-constructor

我基本上会编写以下代码。我明白为什么它不能编译。

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();

有没有办法实现这种行为没有涉及堆分配?

5 个答案:

答案 0 :(得分:10)

有更好,更清晰的方法来解决问题,而不是在堆栈上显式保留空间,例如使用条件表达式。

但是,如果类型不是可构造的,或者你有更复杂的条件意味着真的确实需要在堆栈上保留空间以便稍后在两个不同的地方构建某些东西,你可以使用解决方案如下。

标准库提供aligned_storage特征,因此aligned_storage<T>::type是正确大小的POD类型和用于存储T的对齐方式,因此您可以使用它来保留空间,然后使用placement-new将对象构造到该缓冲区中:

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

请记住手动销毁它,您可以使用unique_ptr和自定义删除器进行销毁:

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

或者使用lambda,虽然这会浪费堆栈上的额外指针; - )

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

甚至只是一个专用的本地类型,而不是unique_ptr

struct Cleanup {
  A* a;
  ~Cleanup() { a->~A(); }
} cleanup = { ptr };

答案 1 :(得分:8)

假设您想多次执行此操作,可以使用辅助函数:

A do_stuff(bool flg)
{
  return flg ? A("foo") : A(42);
}

然后

A instance = do_stuff(something);

否则,您可以使用条件运算符表达式 *

进行初始化
A instance = something ? A("foo") : A(42);

*这是条件运算符“不像if-else”的示例。

答案 2 :(得分:3)

在某些简单的情况下,您可以使用这种标准的C ++语法:

A instance=something ? A("foo"):A(42);

您没有指定您正在使用的编译器,但在更复杂的情况下,使用gcc特定于编译器的扩展可以实现这一点:

A instance=({
       something ? A("foo"):A(42);
});

答案 3 :(得分:2)

这是一项新职位的工作,但如果您重新审视自己的要求,几乎可以肯定会采用更简单的解决方案。

#include <iostream>

struct A
{
    A(const std::string& str) : str(str), num(-1)  {};
    A(const int num)          : str(""),  num(num) {};

    void do_something()
    {
        std::cout << str << ' ' << num << '\n';
    }

    const std::string str;
    const int num;
};

const bool something = true;   // change to false to see alternative behaviour

int main()
{
    char storage[sizeof(A)];
    A* instance = 0;

    if (something)
        instance = new (storage) A("foo");
    else
        instance = new (storage) A(42);

    instance->do_something();
    instance->~A();
}

live demo

通过这种方式,您可以随时构建A,但存储仍然在堆栈中。

但是,你必须自己破坏对象(如上所述),这是令人讨厌的。


免责声明:我的弱放置 - 新示例是天真的,不是特别便携。 GCC自己的Jonathan Wakely发布了一个更好的例子。

答案 4 :(得分:2)

std::experimental::optional<Foo> foo;
if (condition){
  foo.emplace(arg1,arg2);
}else{
  foo.emplace(zzz);
}

然后使用*foo进行访问。 boost::optional如果您没有C ++ 1z TS实现,或者编写自己的optional

在内部,它将使用std对齐存储和bool之类的东西来保护“我已被创建”;或者是union。编译器可能会证明不需要bool,但我对此表示怀疑。

可以从github下载实施,也可以使用boost