下面的代码有问题吗?
#include <iostream>
#include <type_traits>
template <typename T>
void assign_lambda(T&& f)
{
typedef typename std::remove_reference<T>::type functor_type;
typedef typename std::aligned_storage<sizeof(functor_type),
std::alignment_of<functor_type>::value>::type buffer_type;
static char store[sizeof(buffer_type)];
auto const p(new (store) functor_type(std::forward<T>(f)));
(*p)();
}
int main()
{
for (int i(0); i != 5; ++i)
{
assign_lambda([i](){ std::cout << i << std::endl; });
}
return 0;
}
我担心这可能是非标准和/或危险的。
编辑:
为什么要初始化为您要求的char
数组?如果块足够大,可以从堆中分配一个大小为sizeof(buffer_type)
的块,并重复用于重复分配(即避免重复的内存分配)。
void * operator new(std :: size_t size);
效果:由new-expression(5.3.4)调用的分配函数(3.7.4.1),用于分配大小合适的存储字节,以表示该大小的任何对象。
我想如果我从堆中分配,对齐问题就会消失。
答案 0 :(得分:5)
您必须确保store
具有functor_type
的正确对齐方式。除此之外,我没有看到任何有关标准一致性的问题。但是,您可以通过使数组非静态来轻松解决多线程问题,因为sizeof
给出了编译时常量。
§5.3.4,14:
需要对齐[注意:当分配函数返回null以外的值时,它必须是指向已保留对象空间的存储块的指针。假设存储块被适当地对准并且具有所请求的大小。 [...] - 注意事项]
还有另一段关于对齐的§3.7.4.1,但该段明确地不适用于新的放置(§18.6.1.3,1)。
要使对齐正确,您可以执行以下操作:
template <typename T>
void assign_lambda(T&& f)
{
typedef typename std::remove_reference<T>::type functor_type;
//alignas(functor_type) char store[sizeof(functor_type)];
std::aligned_storage<sizeof(functor_type),
std::alignment_of<functor_type>::value>::type store;
auto const p(new (&store) functor_type(std::forward<T>(f)));
(*p)();
//"placement delete"
p->~functor_type();
}
<强>更新强> 上面显示的方法与仅使用正常变量没有区别:
template <typename T>
void assign_lambda(T&& f)
{
typedef typename std::remove_reference<T>::type functor_type;
functor_type func{std::forward<T>(f)};
func();
}
如果 是函数内部的静态变量,则需要一个不可分配的仿函数的RAII包装器。只是放置新功能是不够的,因为仿函数不会被正确销毁,并且它们拥有的资源(例如通过捕获的智能指针)将不会被释放。
template <typename F>
struct RAIIFunctor {
typedef typename std::remove_reference<F>::type functor_type;
std::aligned_storage<sizeof(functor_type),
std::alignment_of<functor_type>::value>::type store;
functor_type* f;
RAIIFunctor() : f{nullptr} {}
~RAIIFunctor() { destroy(); }
template <class T>
void assign(T&& t) {
destroy();
f = new(&store) functor_type {std::forward<T>(t)};
}
void destroy() {
if (f)
f->~functor_type();
f = nullptr;
}
void operator() {
(*f)();
}
};
template <typename T>
void assign_lambda(T&& f)
{
static RAIIFunctor<T> func;
func.assign(std::forward<T>(f));
func();
}
您可以在行动here
中查看代码答案 1 :(得分:3)
我不明白。为什么只使用aligned_storage
来创建一些未初始化的存储,而不是......使用它提供的对齐存储?这就像从柏林到里斯本乘坐柏林 - &gt;里斯本航班随后是里斯本 - &gt;莫斯科航班。
typedef typename std::remove_reference<T>::type functor_type;
typedef typename std::aligned_storage<sizeof(functor_type),
std::alignment_of<functor_type>::value>::type buffer_type;
static buffer_type store;
auto const p(new (&store) functor_type(std::forward<T>(f)));
答案 2 :(得分:2)
除了已经提到的对齐问题之外,您还要通过展示位置new
创建lambda的副本,但不是要销毁副本。
以下代码说明了问题:
// This class plays the role of the OP's lambdas
struct Probe {
Probe() { std::cout << "Ctr" << '\n'; }
Probe(const Probe&) { std::cout << "Cpy-ctr" << '\n'; }
~Probe() { std::cout << "Dtr" << '\n'; }
};
// This plays the role of the OP's assign_lambda
void f(const Probe& p) {
typedef typename std::aligned_storage<sizeof(Probe),
std::alignment_of<Probe>::value>::type buffer_type;
static buffer_type store;
new (&store) Probe(p);
}
int main() {
Probe p;
// This plays the role of the loop
f(p);
f(p);
f(p);
}
输出结果为:
Ctr
Cpy-ctr
Cpy-ctr
Cpy-ctr
Dtr
因此,构造了4个对象,只有一个被销毁。
此外,在OP的代码中store
是static
,这意味着一个lambda重复构造在另一个上面,好像后者只是原始内存。