lambda函数赋值解决方法
lambda functors assignment workaround
下面的代码有问题吗?
#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*运算符new(std::size_t size);
效果:由新表达式(5.3.4)调用的分配函数(3.7.4.1),用于分配适当对齐的存储大小字节,以表示该大小的任何对象。
我想如果我从堆中分配,那么对齐问题就会消失。
您必须确保store
与functor_type
正确对齐。除此之外,我没有看到任何关于标准一致性的问题。但是,您可以通过使数组非静态来轻松解决多线程问题,因为sizeof
提供了compiletime常量。
§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包装。仅仅放置newing是不够的,因为函数不会被正确销毁,它们所拥有的资源(例如通过捕获的智能指针)也不会被释放。
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();
}
你可以在这里看到代码的作用
我不明白。为什么使用aligned_storage
只是为了获得一些大小来创建未初始化的存储,而不是。。。使用它提供的对齐存储?这几乎就像从柏林到里斯本,先乘坐柏林->里斯本的航班,然后乘坐里斯本->莫斯科的航班。
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)));
除了前面提到的对齐问题外,您正在通过放置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被重复构建在另一个之上,就好像后者只是原始内存一样。
- 有没有办法在 c++ 中同时生成随机数?如果没有,是否有解决方法?
- 使用 Git 处理 C++ Visual Studio 2019 解决方案的外部依赖项源代码管理的最佳方法是什么?
- 在 c++ 中解决段树以外的范围查询的有效方法是什么?
- 此解决方案中生成更改的方法数量(自上而下)有什么问题?
- 从 int 中剥离位时,编译器会警告一个转换,但不警告其他转换.有解决方法吗?
- 是否有解决方法可以在 c++ 中为 short 定义用户定义的文字?
- 不为 emplace() 定义构造函数的解决方法
- 当只有静态方法受到影响时,如何解决C++中的链接器错误?
- 删除复制构造函数的 Intel 13.1.2 中不良C++行为的解决方法
- 函数模板部分专业化-有什么解决方法吗
- 带boost的过载模糊性:可选,解决方法
- 继承构造函数和其他变量的解决方法
- 解决虚拟方法的歧义继承的两种方法
- 解决歧义的方法
- C++ 解决方法:"从类型"B*"的表达式初始化类型"C*&"的引用无效"
- 对前向声明类型进行unique_ptr的解决方法
- 必须使用尾随返回类型的示例,因为无法用旧方法解决问题
- 如何通过动态规划方法解决这个问题?
- extern可以解决这个问题吗,或者我可以通过其他方法解决这个问题吗?
- 从析构函数调用虚拟方法-解决方法