Я хотел бы разместить лямбду, которая захватила уникальный указатель в контейнер, но не удалось скомпилировать под GCC 7.3.0 как C ++ 17. Из сообщений об ошибках он вызывает конструктор копирования для unique_ptr при построении функции.
#include <memory>
#include <functional>
#include <string>
#include <queue>
int main (){
auto ptr = std::make_unique<std::string>("123456");
auto l = [p = std::move(ptr)] () mutable {
p.reset();
};
std::queue<std::function<void()>> q;
q.emplace(std::move(l));
return 0;
}
На самом деле у меня много лямбда-выражений с одним и тем же списком параметров и одним и тем же типом возвращаемого значения, и мне нужно разместить их в один контейнер. Поэтому мне нужно обернуть общий тип, например std::function<void()>.
Кроме того, я не хочу добавлять что-то еще, например, использование std::shared_ptr чтобы сделать его копируемым, или использовать другую лямбду для повторного переноса.
На основании вышеизложенного я разработал LambdaWrapper использовать вместо std::function. Захваченная переменная не будет скопирована из-за некопируемого LambdaWrapper.
class LambdaWrapper {
public:
template <class T, typename = std::is_invocable<decltype(std::mem_fn(&T::operator()))>>
LambdaWrapper(T&& lambda) {
MemberFunction memfn;
// 此处强制转换参考std::_Function_base::_Base_manager::_M_init_functor
// This forced type conversion is refers to std::_Function_base::_Base_manager::_M_init_functor
new (&memfn)(decltype(&T::operator()))(&T::operator());
// member function pointer有16B,前8B是函数地址,后8B是this指针的偏移。因为lambda对象没有继承,所以偏移为0,可忽略
// Member function pointer is 16B. The first 8B is the address of function, and the last 8B is the offset of this pointer. Because the lambda type is not inherited from other classes, the offset is 0 and it can be ignored.
new (&func_) void*(memfn.func);
new (&deleter_)(void (*)(T*))(&DeleterImpl<T>::f);
data_ = malloc(sizeof(T));
new (data_) T(std::forward<T>(lambda));
}
~LambdaWrapper() {
if (data_ != nullptr && deleter_ != nullptr) {
deleter_(data_);
} else if (data_ != nullptr || deleter_ != nullptr) {
// something wrong
}
}
void operator()() { func_(data_); }
LambdaWrapper(LambdaWrapper&& T) {
data_ = T.data_;
func_ = T.func_;
deleter_ = T.deleter_;
T.data_ = nullptr;
T.func_ = nullptr;
T.deleter_ = nullptr;
}
LambdaWrapper& operator=(LambdaWrapper&& T) {
data_ = T.data_;
func_ = T.func_;
deleter_ = T.deleter_;
T.data_ = nullptr;
T.func_ = nullptr;
T.deleter_ = nullptr;
return *this;
}
private:
LambdaWrapper(const LambdaWrapper&) = delete;
LambdaWrapper& operator=(const LambdaWrapper&) = delete;
struct MemberFunction {
void* func;
void* ptr;
};
void (*func_)(void*);
void (*deleter_)(void*);
void* data_;
template <class T>
struct DeleterImpl {
static void f(T* t) { delete t; }
};
};
Наконец, есть ли лучшее решение? И есть ли проблема с LambdaWrapper?
