如何在函数调用之后初始化变量,例如新表达式提供
How to initialize a variable after a function call, like the new-expression provides?
当前我正在处理自定义内存分配,而缺点之一是我必须编写多行才能达到与新表达式提供的相同结果只有一个简单的电话。
简单的初始化:
MyClass *obj = new MyClass(3.14);
不太简单的初始化:
void *obj_mem = alloc->Allocate(sizeof MyClass, alignof(MyClass));
MyClass *obj = new(obj_mem) MyClass(3.14);
我将向我的项目组提供诸如该项目的分配器,并希望它们实际使用它们,而不是回到调用new
,因为我们需要这些更快的分配器来管理我们的内存。
但是,要实现这一目标,我将不得不设计最简单的语法,以用我的自定义分配器初始化变量。
我的解决方案
我最好的选择是每个类都覆盖operator new
,因为它是新表达的分配函数。
class MyClass
{
...
void* operator new(size_t size, Allocator *alloc)
{
return alloc->Allocate(size, alignof(MyClass));
}
}
,然后是初始化变量的语法成为我最终想要的:
MyClass *obj = new(alloc) MyClass(3.14);
但是,如果我可以将上述一般等效,那就太好了。因此,我不必为每个班级覆盖operator new
。
完全杀死 new
。无论如何,您都必须将创造与破坏捆绑在一起。
template<class T>
struct destroy {
Alloc* pool = nullptr;
void operator()(T* t)const {
ASSERT(t);
t->~T();
ASSERT(alloc);
alloc->Dealloc( t );
}
};
template<class T>
using my_unique_ptr = std::unique_ptr<T, destroy<T>>;
namespace details{
template<class T, class...Args>
my_unique_ptr<T> my_make_unique( Alloc* alloc, Args&&...args ) {
void* p_data = alloc->Allocate(sizeof(T), alignof(T));
try {
T* ret = ::new(p_data) T(std::forward<Args>(args)...);
return {ret, destroy<T>{alloc}};
} catch (...) {
alloc->Dealloc( p_data );
throw;
}
}
}
/// usual one:
template<class T, class...Args>
my_unique_ptr<T> my_make_unique( Alloc* alloc, Args&&...args ) {
return details::my_make_unique<T>( alloc, std::forward<Args>(args)... );
}
// permit leading il:
template<class T, class U, class...Args>
my_unique_ptr<T> my_make_unique( Alloc* alloc, std::initializer_list<U> il, Args&&...args ) {
return details::my_make_unique<T>( alloc, il, std::forward<Args>(args)... );
}
// for {} based construction:
template<class T>struct tag_t{using type=T;};
template<class T>using no_deduction=typename tag_t<T>::type;
template<class T>
my_unique_ptr<T> my_make_unique( Alloc* alloc, no_deduction<T>&&t ) {
return details::my_make_unique<T>( alloc, std::move(t) );
}
现在my_make_unique
采用Alloc*
和施工参数,它返回带有破坏代码的智能指针。
这个独特的指针可以隐式传递给std::shared_ptr<T>
(通过移动)。
如果您对这种初始化形式感到满意:
MyClass *obj = new(alloc) MyClass(3.14);
也就是说,如果您不得不将这样的对象传递给new
,那么您可以全球过载new
带有放置参数:
void* operator new(std::size_t size, Allocator *alloc)
{
return alloc->Allocate(size, alignof(std::max_align_t));
}
任何非阵列使用new(alloc)
都会引起对此功能的调用。您的最大问题是没有匹配的"位置删除"语法。因此,要正确删除对象,您将需要执行通常的其他用法,以通过新的位置创建的对象:
obj->~Typename();
::operator delete(obj, sizeof(obj), alloc);
当然,这需要operator delete
超载:
void operator delete(void *obj, std::size_t sz, Allocator *alloc)
{
alloc->Deallocate(obj, sz, alignof(std::max_align_t));
}
如果您可以访问C 17,则operator new
和operator delete
可以将对象的实际对齐方式作为参数。
您可以制作出工厂模板功能。草图:
template<class R,class... T>
R* create(alloc& alloc,T&&... t)
{
void *obj_mem = alloc.Allocate(sizeof(R), alignof(R));
R* obj = new(obj_mem) R(std::forward<T>(t)...);
return obj:
}
如果可以(并且想)修改分配器,我会添加一个成员函数以分配和构造。在class SomeAllocator
中:
template <typename T, typename... Args>
T* SomeAllocator::Construct(Args&&... args)
{
auto ptr = Allocate(sizeof(T), alignof(T));
return new (ptr) T(std::forward<Args>(args)...);
}
这将使您像
一样使用它auto obj = alloc->Construct<MyClass>(3.14);
对于破坏和删除,您可以 - 提供统一的语法 - 添加Destruct
方法:
template <typename T>
void SomeAllocator::Destruct(T*& t)
{
t->~T();
Deallocate(t, sizeof(T), alignof(T);
t = nullptr;
}
- 错误:constexpr 变量'struct2Var'必须由常量表达式初始化
- std::cout 在打印变量与函数表达式时的行为不同
- 如何在 lambda 表达式中传递变量?
- 表达式必须具有常数值,变量不能用作定义数组大小的常数
- C++ 在方程中使用变量;错误:表达式必须具有整数或无作用域枚举类型及其他
- 代码样式:在 switch/if 语句的分支中重用控件表达式或控制变量
- 编译器在传递 const 变量时返回错误:模板参数不是常量表达式
- C++ 使用变量而不是常量表达式初始化数组
- lambda 表达式中引用捕获的 constexpr 变量和非显式捕获的 constexpr 变量之间的区别
- 如何从字符串变量为正则表达式构建原始字符串
- 变量在常量表达式中可用的条件
- QT连接:使用lambda表达式重置通过了int变量(怪异)
- 通过引用传递表达式与通过引用传递变量
- 错误!Constexpr变量必须通过常数表达式constexpr初始化
- 没有变量声明为函数,但错误:二进制表达式的操作数无效
- 常量表达式中引用类型的变量
- 如何将表达式转换为变量
- 从C++调用 Python 或 Lua 来计算表达式,仅在需要时计算未知变量
- ExprTK 未知变量分辨率取决于表达式类型
- C lambda表达式变量