如何存储多态闭包?

时间:2013-10-29 23:32:18

标签: c++ lambda polymorphism c++14

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除外)?
  • 希望这样的事情是可能的,或者这种事情会不会与静态类型不相容,这是否合理?

3 个答案:

答案 0 :(得分:3)

没有。好吧也许吧。

对于您的特定情况,您的lambda只是实例化时已知的单个函数f的覆盖集。可以创建覆盖集对象并使用类型擦除传递而不会出现太多问题。您只需手动枚举覆盖并将其提供给覆盖集。

因此,如果你的目标只是拥有一个覆盖集f的对象,是的,你可以这样做。请参阅"Manual" signature overload resolution - 在这个混乱的基础上添加一些类型的擦除,bob是你的叔叔。

一般情况下,你有一些auto lambda,里面有任意代码,没有。

设想这个问题的方法是想象一个用你的lambda编译的DLL或共享库,第二个DLL或共享库,保存function类似的对象,以及一些其他想要调用它的DLL或共享库。 / p>

调用function时发生的行为取决于lambda 的定义和你要用它调用任意程度的类型。

为了使其工作,必须在创建lambda的DLL和调用它的类型的DLL中使用几乎完整的运行时编译模型,并且运行时编译模型将必须兼容。

这不是C ++标准所要求的,如果是这样的话会使事情变得复杂得多,并且会消除优化机会。

现在,并非一切都没有希望。

如果您希望支持某些类型的固定列表,则可以编写多态function签名。这基本上是上面“覆盖集”解决方案的一个特例,甚至可以用它来编写。

另一方面,如果您愿意键入擦除lambda参数的属性,并键入erase,并返回一些统一类型(无论是boost::any还是boost::variant或其他) ,你可以做点什么。您编写了类型擦除对象类型,并将其公开。然后你有一个std::function< boost::any(type_erasure_object) >,并且转换发生在调用之外,并且在调用中你处理所述类型的擦除对象。

使用类型擦除对象选择重载非常棘手,因为C ++编译器对生成要考虑的重载列表没有多大帮助。如果您手动收集该列表,您甚至可以键入擦除您将选择的过载。

可以将其拉下来,但我之前没有写过。替代方案都容易得多。

我不考虑类型擦除的情况来解决这个问题,因为它会阻止某些类型的优化。但从理论上讲,这意味着你可以使用几乎任意的类型。

类型擦除对象必须暴露给最终用户,并且必须删除 每个 lambda的每个类型信息<{1}}需要知道。因此,在某些情况下,这可以显着限制您在std::vector中存储的lambda。

有关如何键入擦除几乎任意对象的示例,请查看boost类型擦除。

最后,您要求的内容很少是问题的实际要求。你最好描述你实际的,实际的问题,几乎可以肯定的解决方案并不像上面那样深奥。

答案 1 :(得分:1)

所谓的通用lambda 的类型是具有成员模板operator()的类类型。当需要转换时,必须知道实际类型。对于非捕获通用lambda,当前的标准草案甚至包含一个示例:

auto glambda = [](auto a) { return a; };
int (*fp)(int) = glambda;

这与从普通函数模板形成函数指针没什么不同。

对于一般的通用lambda,我认为期望可调用对象的转换会触发正确的模板特化,因此std::function<int(int)> f(glambda);应该可以正常工作。

答案 2 :(得分:1)

  

我想要存储的是不同类型的对象,但每个对象都可以使用一组可能无限的参数类型进行调用。

翻译单位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>实例化的时间和地点,以便可以调用它?

使用参数v.front()调用lives_in_a {}时?在这种情况下,看不到lives_in_b的定义,甚至没有实例化的定义。

调用v.push_back(lives_in_b {})时?在那种情况下,看不到lives_in_a的定义,因此可能实例化的内容并不多。

这是一个演示,C ++的编译模型和模板实例化的方式的特定组合,不允许该特定的愿望。它与静态类型关系不大。