gpt4 book ai didi

c++ - 我该如何创建一个类,以对对象进行类型删除,直到调用了一个函数,而没有事先指定可能的函数列表?

转载 作者:太空狗 更新时间:2023-10-29 20:58:51 25 4
gpt4 key购买 nike

背景

标题听起来似乎很困惑,所以让我解释一下。首先,这是minimal version of my implementation,因此您可以更轻松地遵循这些概念。如果您看过Sean Parent的一些演讲,您会知道他想出了一种抽象多态性的方法,允许这样的代码:

std::vector<Drawable> figures{Circle{}, Square{}};
for (auto &&figure : figures) {draw(figure);}

请注意,没有指针或任何东西。在 draw上调用 Drawable将在包含的对象上调用适当的 draw函数,而对象的类型不易于访问。一个主要的缺点是必须为每个任务编写类似于 Drawable的类。我试图对此进行抽象,以便该类不必知道该函数。我当前的解决方案如下:
std::vector<Applicator<Draw>> figures{Circle{}, Square{}};
for (auto &&figure : figures) {figure.apply(Draw{});}

在这里, Draw是带有 operator()(Circle)opeator()(Square)的泛函,或者是通用版本。这样,这也是一种访客模式实现。例如,如果您还想打印每个图形的名称,则可以执行 Applicator<Draw, PrintName>。调用 apply时,将选择所需的功能。

我的实现通过将可调用类型的 boost::variant传递给虚函数并使其访问该变量并在其中调用该函数来工作。总的来说,我会说这种实现是可以接受的,但是我还没有考虑允许任何数量的参数或返回类型,更不用说因函数而异的参数了。



我花了几天的时间来尝试一种无需将 Applicator用作模板即可进行这项工作的方法。理想情况下,用法将与此类似。为了简单起见,假设调用的函数必须具有签名 void(ObjectType)
//For added type strictness, I could make this Applicator<Figure> and have 
//using Figure<struct Circle> = Circle; etc
std::vector<Applicator> figures{Circle{}, Square{}};
for (auto &&figure : figures) {figure.apply(Draw{});} //or .apply(draw); if I can

问题通常归结为以下事实:对象类型只能在调用它的函数中获得。在内部,该类使用虚函数,这意味着没有模板。调用 apply时,会发生以下情况(与肖恩的讲话相同):
  • 在具有派生类的运行时类型的指向基类的指针上调用内部基类的apply。
  • 调用被调度到派生类,派生类知道存储对象的类型。

  • 因此,当我有了对象时,必须将调用的函数简化为类中已知的单个类型,该类既知道要调用的函数又接受对象。我无法为自己的生活想出办法。

    尝试次数

    这是几次失败的尝试,因此您可以了解为什么我觉得很困难:

    前两个函数的前提是要拥有一个保存函数调用的类型,该函数调用要减去未知的第一个参数(存储的对象)。这至少需要在可调用对象的类型上进行模板化。通过使用Sean Parent的技术,可以很容易地制作一个可以存储在 FunctionCall<F>中的 GenericFunctionCall类,就像 Circle中的 Figure一样。可以将此 GenericFunctionCall传递到虚函数中,而另一个则不能。

    尝试1
  • apply()用已知的可调用对象类型进行调用。
  • 可调用对象的类型用于创建FunctionCall<Type>并将其存储为类型擦除的GenericFunctionCall
  • GenericFunctionCall对象传递给虚拟apply函数。
  • 派生类获取调用对象,并将该对象用作第一个可用参数。
  • 出于不允许将虚函数用作模板的相同原因,GenericFunctionCall可以在右侧FunctionCall<Type>上调用必要的函数,但不能转发第一个(存储的对象)参数。

  • 尝试2

    作为尝试1的延续:
  • 为了将存储的对象传递到GenericFunctionCall上调用的函数中,可以将存储的对象类型擦除为GenericObject
  • 可能有以下两种情况之一:
  • 调用了一个函数,并为其指定了适当的FunctionCall<Type>,但是给了它一个GenericObject,在其上调用了函数,其类型未知。回想一下,该函数无法在函数调用类型上进行模板化。
  • 会调用一个函数,并为其指定一个表示存储对象的正确T,但具有一个GenericFunctionCall可从中提取正确的函数调用类型。我们回到了派生类的apply函数的起点。

  • 尝试3
  • 在调用apply时采用已知类型的可调用对象,并使用它来制作一些存储可以调用的函数的函数,该函数可以使用已知的存储对象类型(例如std::function)进行调用。
  • 将其键入到boost::any中,然后将其传递给虚拟函数。
  • 当派生类中已知存储的对象类型时,将其强制转换回适当的类型,然后将对象传递给它。
  • 意识到,这整个方法要求在调用apply时知道存储的对象类型。


  • 是否有任何聪明的主意可以将此类转变为不需要模板参数的类,而是可以采用任何可调用对象并与存储对象一起调用它?

    附言我愿意征询比 Applicatorapply更好的名称的建议。

    最佳答案

    这是不可能的。考虑一个由三个翻译单元组成的程序:

    // tu1.cpp
    void populate(std::vector<Applicator>& figures) {
    figures.push_back(Circle{});
    figures.push_back(Square{});
    }

    // tu2.cpp
    void draw(std::vector<Applicator>& figures) {
    for (auto &&figure : figures) { figure.apply(Draw{}); }
    }

    // tu3.cpp
    void combine() {
    std::vector<Applicator>& figures;
    populate(figures);
    draw(figures);
    }

    每个TU必须确实可以因果隔离地单独翻译。但是,这意味着没有一个编译器可以同时访问 DrawCircle,因此永远不会生成 Draw调用 Circle::draw的代码。

    关于c++ - 我该如何创建一个类,以对对象进行类型删除,直到调用了一个函数,而没有事先指定可能的函数列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26303718/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com