我如何在堆栈上为非默认构造体保留 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
,但我对此表示怀疑。
我基本上会写下面这段代码。我明白为什么它不能编译。
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
,但我对此表示怀疑。