如何存储多态闭包
How store polymorphic closures?
>C++1y 提供多态 lambda(即使用 auto
作为参数类型的一部分):
int f(int);
double f(double);
std::string f(const std::string&);
auto funcObj = [](const auto& param){ return f(param); }
存储由 lambda 生成的闭包很容易,如图所示:只需使用 auto
变量。但假设我想创建此类对象的vector
。vector
是什么类型?通常的答案是使用 std::function
,但这在这种情况下不起作用,因为 AFAIK 没有多态std::function
这样的东西,即这在 C++1y 中是不合法的:
std::vector<std::function<auto(const auto&)>> vecOfPolymorphicClosures;
如果这是合法的,那么你可以做一些事情,比如创建一个回调容器,每个回调都可以用任何一组参数调用,每个参数都可以返回一个依赖于所传递参数类型的类型。任何给定回调的结果都可以存储在auto
变量中,至少在理论上是这样。
两个问题:
- 有没有办法在 C++1y 中声明一个变量或容器,该变量或容器可以容纳不同类型的多态 lambda(除了像
boost::any
这样的东西)? - 希望这样的事情是可能的,还是这种事情与静态类型不兼容?
No. 好吧,也许吧。
对于您的特定情况,您的 lambda 只是单个函数的覆盖集,f
实例化时已知。 可以使用类型擦除创建和传递覆盖集对象,而不会有太大问题。 只需手动枚举替代并将其提供给覆盖集。
因此,如果您的目标是拥有一个对象,即f
的覆盖集,是的,您可以这样做。 请参阅"手动"签名重载分辨率 - 在混乱之上添加某种类型擦除,Bob 就是您的叔叔。
一般情况下,您有一些包含任意代码的 lambda auto
,没有。
设想此问题的方法是想象一个用你的lambda编译的DLL或共享库,第二个DLL或共享库保存类似function
的对象,以及其他一些DLL或共享库想要调用它。
调用 function
时发生的行为取决于 lambda 的定义以及要调用它的任意程度的类型。
为了使其正常工作,几乎完整的运行时编译模型必须在创建 lambda 的 DLL 和调用它的类型的 DLL 中都可用,并且该运行时编译模型必须兼容。
这既不是C++标准所要求的,而且如果这样做会使事情变得更加复杂,并且会消除优化机会。
现在,并非一切都是没有希望的。
如果有一些要支持的固定类型列表,则可以编写多态function
签名。 这基本上是上面"覆盖集"解决方案的特例,甚至可以使用它编写。
另一方面,如果您愿意将参数的属性键入 erase 到您的 lambda,并键入 erase,并返回一些统一的类型(无论是 boost::any
还是 boost::variant
或其他什么),您可以做一些事情。 您编写了一个类型擦除对象类型,并公开了它。 然后你有一个std::function< boost::any(type_erasure_object) >
,转换发生在调用之外,在调用中你处理所述类型的擦除对象。
使用类型擦除对象选取重载很棘手,因为C++编译器在生成要考虑的重载列表方面没有多大帮助。 如果您手动收集该列表,您甚至可以键入 erase 您将选择的重载。
实现它是可能的,但我以前没有写过。 其他方法都容易得多。
我不认为类型擦除大小写可以解决这个问题,因为它阻止了某些类型的优化。 但从理论上讲,这意味着您可以使用几乎任意的类型。
类型纠删对象必须向最终用户公开,并且它必须擦除您塞入该std::vector
的每个 lambda 需要知道的每条类型信息。 因此,在某些情况下,这会显着限制您存储在std::vector
中的 lambda。
有关如何键入擦除几乎任意对象的示例,请查看增强类型擦除。
最后,你所要求的很少是问题的实际要求。 你最好描述一下你实际的实际问题,几乎可以肯定它的解决方案不像上面那么深奥。
我想存储的是不同类型的对象,但每个对象都可以使用一组可能无限的参数类型来调用。
翻译单元 A:
// a.cpp
#include <cassert>
std::vector<magical_type> v;
struct lives_in_a { int i; };
// defined in TU B:
void prepare();
int main()
{
prepare();
assert( v.front()(lives_in_a { 42 }) == 42 );
}
翻译单元 B:
// b.cpp
struct lives_in_b {
template<typename Anything>
int operator()(Anything const& a) const
{ return a.i; }
};
void prepare()
{
// ignore global initialization order fiasco for the sake
// of the argument
extern std::vector<magical_type> v;
v.push_back(lives_in_b {});
}
何时何地lives_in_b::operator()<lives_in_a>
实例化,以便可以调用它?
当用参数lives_in_a {}
调用v.front()
时?在这种情况下,看不到lives_in_b
的定义,甚至很少实例化。
什么时候叫v.push_back(lives_in_b {})
?在这种情况下,看不到lives_in_a
的定义,因此可能的实例化无能为力。
这表明C++的编译模型和模板实例化工作方式的特定组合不允许该特定愿望。它与静态类型关系不大。
所谓的泛型 lambda 的类型是具有成员模板operator()
的类类型。当需要转换时,必须知道实际类型。对于非捕获通用 lambda,当前标准草案甚至包含以下示例:
auto glambda = [](auto a) { return a; };
int (*fp)(int) = glambda;
这与从普通函数模板形成函数指针没有什么不同。
对于一般的通用 lambda,我认为期望可调用对象的转换将触发正确的模板专用化,因此std::function<int(int)> f(glambda);
应该可以正常工作。
- 多态性和功能结合
- 具有默认模板参数的多态类的模板推导失败
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 多态二进制函数
- 访问存储在向量C++中的结构的多态成员
- 使用取消引用的指针的多态性会产生意外的结果.为什么?
- 将 std::allocate_shared 与多态资源分配器一起使用
- 如何将C++闭包与变量参数同时重用——类似于JavaScript
- 通过switch和static_cast访问多态对象的运行时类型
- C++boost序列化多态性问题
- 多态杆件变量 - 类设计
- 如何查找哪个类对象位于数组的特定索引上(多态性)
- 如何在多线程中正确使用unique_ptr进行多态性?
- Doees the 'this' 指针参与虚函数的多态行为
- C++ 在堆栈中包含多态属性的类对象存储
- 基类和派生类的多态赋值运算符
- 转身多态对象
- 如何在基类指针向量的元素上应用重载的多态函数
- 为什么 std::remove_if 会创建如此多的闭包?
- 如何存储多态闭包