将多种类型的 lambda 函数存储在一种类型的变量中
Storing multiple type lambda functions in one type of variable
我有自己的Runnable
类,可以存储函数指针,成员函数(可能是lambda)指针或函子,并以一种方式运行它。这是它的简化版本:
template<typename returnType, typename... args>
class Runnable
{
private:
template<typename rreturnType, typename... aargs>
struct FuncBase
{
virtual returnType run(aargs...) const = 0;
virtual FuncBase<rreturnType, aargs...>* clone() const = 0;
virtual ~FuncBase() { }
};
template<typename rreturnType, typename... aargs>
struct Func : FuncBase<rreturnType, aargs...>
{
rreturnType (*func) (aargs...);
rreturnType run(aargs... arg) const { return func(arg...); }
FuncBase<rreturnType, aargs...>* clone() const { return new Func<rreturnType, args...>(func); }
Func(rreturnType (*func) (aargs...)) : func(func) { }
};
template<typename memberType, typename rreturnType, typename... aargs>
struct MemberFunc : public FuncBase<rreturnType, aargs...>
{
memberType& owner;
rreturnType (memberType::*member) (args...);
rreturnType run(aargs... arg) const { return (owner.*member)(arg...); }
FuncBase<rreturnType, aargs...>* clone() const { return new MemberFunc<memberType, rreturnType, aargs...>(owner, member); }
MemberFunc(memberType& owner, rreturnType (memberType::*member) (aargs...)) : owner(owner), member(member) { }
};
template<typename functorType, typename rreturnType, typename... aargs>
struct FunctorFunc : public FuncBase<rreturnType, aargs...>
{
functorType& owner;
rreturnType run(aargs... arg) const { return owner(arg...); }
FuncBase<rreturnType, aargs...>* clone() const { return new FunctorFunc<functorType, rreturnType, aargs...>(owner); }
FunctorFunc(functorType& owner) : owner(owner) { }
};
FuncBase<returnType, args...>* f;
Runnable(const Runnable<returnType, args...>& r) = delete;
Runnable<returnType, args...>& operator=(const Runnable<returnType, args...>& r) = delete;
public:
Runnable(returnType (*func) (args...)) { f = new Func<returnType, args...>(func); }
template<typename memberType>
Runnable(memberType& owner, returnType (memberType::*member) (args...)) { f = new MemberFunc<memberType, returnType, args...>(owner, member); }
template<typename functorType>
Runnable(functorType& owner) { f = new FunctorFunc<functorType, returnType, args...>(owner); }
~Runnable() { delete f; }
returnType operator() (args... arg) const { return f->run(arg...); }
};
我想以同样的方式存储可变的 lambda 函数
我可以存储普通的lambda,但是当我尝试存储可变的lambda时,例如:
int fk = 20;
Runnable<double, double> r([&](double d) mutable -> double { cout << fk; return d; });
r(1.0);
我收到以下错误:
no matching function for call to 'Runnable<double, double>::Runnable(main()::__lambda0)'
Runnable<double, double> r([&](double d) mutable -> double { cout << fk; return d; });
当我从 Runnable::FunctorFunc 中删除引用时
template<typename functorType, typename rreturnType, typename... aargs>
struct FunctorFunc : public FuncBase<rreturnType, aargs...>
{
functorType owner;
rreturnType run(aargs... arg) { return owner(arg...); }
FuncBase<rreturnType, aargs...>* clone() const { return new FunctorFunc<functorType, rreturnType, aargs...>(owner); }
FunctorFunc(functorType owner) : owner(owner) { }
};
它在可变的 lambda 中是正确的,但我从普通 lambda 中得到以下错误:
cannot allocate an object of abstract type 'Runnable<double, double>::FunctorFunc<tsf::easing::__lambda0, double, double>'
Runnable(functorType owner) { f = new FunctorFunc<functorType, returnType, args...>(owner); }
^
^
主要问题是我如何修改这个类以接受 lambda。或者也许有人对这个问题有类似的类或其他解决方案
lambda 表达式的计算结果是临时值。在函子对象的构造函数中
template<typename functorType> Runnable(functorType& owner) { /*...*/ }
您正在尝试将非常量左值引用绑定到此类临时引用。这是行不通的,无论 lambda 是否可变,它都不起作用。
我猜它似乎适用于不可变的 lambda,因为你使用了没有捕获任何东西的 lambda;这样的对象隐式可转换为函数指针,因此调用实际上解析为采用函数指针的构造函数。
您可以声明构造函数以改为采用转发引用,但您必须实际将参数复制或移动到FunctorFunc
的owner
成员中。该成员不能是引用,因为一旦对构造函数的调用完成,它可能会悬而未决 - 如果它是一个 lambda 表达式,则参数是临时的,还记得吗?
这是一个修改后的FunctorFunc
,它执行我上面描述的操作:
template<typename functorType, typename rreturnType, typename... aargs>
struct FunctorFunc : public FuncBase<rreturnType, aargs...>
{
functorType owner;
rreturnType run(aargs... arg) { return owner(arg...); }
FuncBase<rreturnType, aargs...>* clone() const { return new FunctorFunc<functorType, rreturnType, aargs...>(owner); }
FunctorFunc(functorType&& owner_) : owner(std::move(owner_)) {}
FunctorFunc(const functorType& owner_) : owner(owner_) {}
};
这是修改后的构造函数:
template<typename functorType>
Runnable(functorType&& owner) { f = new FunctorFunc<functorType, returnType, args...>(std::forward<functorType>(owner)); }
密切关注我必须进行的所有更改 - 它们都是必不可少的:在哪里声明为引用,在哪里不是,使用 std::forward
和 std::move
。如果有什么不清楚的地方,请问我。该解决方案处理右值和左值可调用对象;右值通常是 lambda,而左值可能是您单独构造的一些其他类型的可调用对象。
请注意,我还必须从 run()
成员函数的声明中删除 const
限定符。这就是mutable
部分的用武之地:不可变 lambda 的函数调用运算符是 const
,因此可以在 const 函子对象上调用它。一旦 lambda 被声明为 mutable
,那就不再是真的了,所以owner
不能再是常量,因此从函数声明中删除了限定符。
现在我们到了第二个问题:您还必须从抽象基类 FuncBase
中的 run()
成员函数的声明中删除 const
限定符。否则,您实际上是在FunctorFunc
中声明一个不同的函数,而不是覆盖基数中的函数。正因为如此,FunctorFunc
仍然是抽象的(它仍然有一个未定义的纯虚函数);这就是错误消息试图告诉您的。
为避免将来遇到此问题,请使用override
说明符 - 它会为您提供明确的错误消息。
编辑:构造函数Runnable(functorType&& owner)
太通用了,在某些情况下它甚至可以充当复制/移动构造函数。这是使用 SFINAE 大锤限制它的解决方案。
最初,我认为我应该将functorType
限制为仅匹配接受args...
并返回returnType
的可调用类型,如当前实例化的模板参数中所述Runnable
。为此,构造函数的模板参数应如下所示:
template<typename functorType, typename = std::enable_if_t<
std::is_same<std::result_of_t<functorType(args...)>, returnType>::value>>
唉,Runnable
的当前实例化也是满足该条件的可调用类型,这正是我们想要避免的。因此,解决方案还必须排除此Runnable
实例化:
template<typename functorType, typename = std::enable_if_t<
!std::is_same<std::remove_cv_t<std::remove_reference_t<functorType>>, Runnable>::value &&
std::is_same<std::result_of_t<functorType(args...)>, returnType>::value>>
具有与当前实例化相同的模板参数的 Runnable
s 将无法通过第一个测试,具有不同模板参数的 Runnable
s 将在第二个测试中失败,任何没有正确参数和返回类型的可调用类型也是如此。类型特征的最大乐趣...
代码假设您使用的是 C++14,否则它会(甚至)更冗长。
- 有没有一种方法可以通过"typedef"为重新定义的基本类型定义特征和强制转换运算符
- 在运行时检查继承是否只有一种类型和 void*
- 文本冒险游戏 - 如何区分一种项目类型与另一种项目类型以及如何构建项目类/子类
- 当 c++ 需要一种数据类型并获取另一种数据类型时会发生什么?
- 在硬件SIMD矢量指针和相应类型之间进行"interpret_cast"是一种未定义的行为吗
- 另一种类型的智能ptr,比如具有弱refs的unique_ptr
- void* 数组将元素转换为另一种类型
- 使用字节向量作为其他类型的原始存储是一种好的做法吗
- 将一种数据类型的向量复制到同一数据类型的结构向量中的有效方法是什么
- 将一种类型的比特重新解释为不同类型的比特的技术
- 有没有一种方法可以使用SFINAE来检测一个类型是否实现了给定的抽象基类
- 在枚举类型上使用std::max是不是一种糟糕的做法
- 为什么需要类型名称,即使似乎足以推断名称应该是一种类型?
- 在 c++ 中将一种结构类型分配给另一种类型
- 将空基类优化对象强制转换为另一种类型是否会破坏严格的别名?
- 我怎样才能让编译器推导出一种类型的 nullptr
- 如何测试指针类型是否可以安全地转换为另一种指针类型?
- 有没有一种很好的方法来实现具有默认失败情况的条件类型?
- 一种类型的多个 cv 分解
- 如何将对象定义为一种类型,然后再将其声明为子类型