背景
标题听起来似乎很困惑,所以让我解释一下。首先,这是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
时,会发生以下情况(与肖恩的讲话相同):因此,当我有了对象时,必须将调用的函数简化为类中已知的单个类型,该类既知道要调用的函数又接受对象。我无法为自己的生活想出办法。
尝试次数
这是几次失败的尝试,因此您可以了解为什么我觉得很困难:
前两个函数的前提是要拥有一个保存函数调用的类型,该函数调用要减去未知的第一个参数(存储的对象)。这至少需要在可调用对象的类型上进行模板化。通过使用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
时知道存储的对象类型。 是否有任何聪明的主意可以将此类转变为不需要模板参数的类,而是可以采用任何可调用对象并与存储对象一起调用它?
附言我愿意征询比
Applicator
和apply
更好的名称的建议。
最佳答案
这是不可能的。考虑一个由三个翻译单元组成的程序:
// 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必须确实可以因果隔离地单独翻译。但是,这意味着没有一个编译器可以同时访问
Draw
和Circle
,因此永远不会生成Draw
调用Circle::draw
的代码。
关于c++ - 我该如何创建一个类,以对对象进行类型删除,直到调用了一个函数,而没有事先指定可能的函数列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26303718/