我如何在堆栈上为非默认构造体保留 space?

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

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

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

有没有办法在不涉及堆分配的情况下实现这种行为

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

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

您没有指定您使用的是哪个编译器,但在更复杂的情况下,可以使用 gcc 编译器特定的扩展来实现:

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

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

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

然后

A instance = do_stuff(something);

否则你可以使用条件运算符表达式进行初始化*:

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

* 这是条件运算符不是 "just like an if-else".

的示例

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

#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 发布了相同想法的更好示例。

有比在堆栈上显式保留 space 更好、更简洁的方法来解决问题,例如使用条件表达式。

然而,如果类型不可移动构造,或者你有更复杂的条件,这意味着你 真的 确实需要在堆栈上保留 space 以便稍后构造一些东西在两个不同的地方,您可以使用下面的解决方案。

标准库提供了aligned_storage trait,这样aligned_storage<T>::type是一个大小和对齐方式都合适的POD类型,用于存储T,所以你可以用它来保留space,然后使用 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 };
std::experimental::optional<Foo> foo;
if (condition){
  foo.emplace(arg1,arg2);
}else{
  foo.emplace(zzz);
}

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

在内部,它将使用类似 std 对齐的存储和 bool 来保护 "have I been created";或者 union。编译器可能会证明不需要 bool,但我对此表示怀疑。

可以从 github or you can use boost 下载实现。