如何实现“虚拟模板功能”在 C++
首先:我已经阅读过并且现在知道虚拟模板成员函数在 C++ 中(还?) 不可能。解决方法是使类成为模板,然后在成员函数中也使用模板参数。
但在 OOP 的背景下,我发现如果该类实际上是一个模板,下面的示例就不会很“自然”。请注意,代码实际上不起作用,但 gcc-4.3.4 报告: error: templates may not be 'virtual'
#include <iostream>
#include <vector>
class Animal {
public:
template< class AMOUNT >
virtual void eat( AMOUNT amount ) const {
std::cout << "I eat like a generic Animal." << std::endl;
}
virtual ~Animal() {
}
};
class Wolf : public Animal {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a wolf!" << std::endl;
}
virtual ~Wolf() {
}
};
class Fish : public Animal {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a fish!" << std::endl;
}
virtual ~Fish() {
}
};
class GoldFish : public Fish {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a goldfish!" << std::endl;
}
virtual ~GoldFish() {
}
};
class OtherAnimal : public Animal {
virtual ~OtherAnimal() {
}
};
int main() {
std::vector<Animal*> animals;
animals.push_back(new Animal());
animals.push_back(new Wolf());
animals.push_back(new Fish());
animals.push_back(new GoldFish());
animals.push_back(new OtherAnimal());
for (std::vector<Animal*>::const_iterator it = animals.begin(); it != animals.end(); ++it) {
(*it)->eat();
delete *it;
}
return 0;
}
因此创建“Fish
因此,我正在寻找一个关于如何实现类似的解决方案,
Fish bar;
bar.eat( SomeAmount food );
这在查看 for 循环时变得特别有用。人们可能想给所有不同的动物喂食特定量(FoodAmount)(通过 eat() 和 bind1st() 例如),这不可能那么容易做到,尽管我觉得这非常直观(因此在某种程度上) “自然”)。虽然现在有些人可能想争辩说这是由于向量的“统一”特征造成的,但我认为/希望应该可以实现这一目标,而且我真的很想知道如何实现,因为这是让我很困惑现在一段时间...
[编辑]
为了澄清我的问题背后的动机,我想编写一个 Exporter 类并让不同的、更专业的类从它派生,而顶级 Exporter 则从中派生。 -class 通常仅用于装饰/结构目的,派生出 GraphExporter 类,该类应再次作为更专业导出的基类但是,与 Animal 示例类似,我希望能够定义。 GraphExporter* 甚至在专门/派生上类(例如在 SpecialGraphExplorer 上),但是当调用“write( out_file )”时,它应该调用 SpecialGraphExporter 的适当成员函数而不是 GraphExporter::write( out_file) 。
也许这让我的处境和意图更加清晰。
最好的,
影子
first off: I have read and I know now that a virtual template member function is not (yet?) possible in C++. A workaround would be to make the class a template and then use the template-argument also in the member-function.
But in the context of OOP, I find that the below example would not be very "natural" if the class was actually a template. Please note that the code is actually not working, but the gcc-4.3.4 reports: error: templates may not be ‘virtual’
#include <iostream>
#include <vector>
class Animal {
public:
template< class AMOUNT >
virtual void eat( AMOUNT amount ) const {
std::cout << "I eat like a generic Animal." << std::endl;
}
virtual ~Animal() {
}
};
class Wolf : public Animal {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a wolf!" << std::endl;
}
virtual ~Wolf() {
}
};
class Fish : public Animal {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a fish!" << std::endl;
}
virtual ~Fish() {
}
};
class GoldFish : public Fish {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a goldfish!" << std::endl;
}
virtual ~GoldFish() {
}
};
class OtherAnimal : public Animal {
virtual ~OtherAnimal() {
}
};
int main() {
std::vector<Animal*> animals;
animals.push_back(new Animal());
animals.push_back(new Wolf());
animals.push_back(new Fish());
animals.push_back(new GoldFish());
animals.push_back(new OtherAnimal());
for (std::vector<Animal*>::const_iterator it = animals.begin(); it != animals.end(); ++it) {
(*it)->eat();
delete *it;
}
return 0;
}
So creating a "Fish< Amount > foo" is kind of strange. However, it seems desirable to me to provide an arbitrary amount of food to eat for each animal.
Thus, I am searching a solution about how to achieve something like
Fish bar;
bar.eat( SomeAmount food );
This becomes particularly useful when looking at the for-loop. One might like to feed a specific amount (FoodAmount) to all of the different animals (via eat() and bind1st() e.g.), it could not be done that easily, although I wound find this very inuitive (and thus to some extent "natural). While some might want to argue now that this is due to the "uniform"-character of a vector, I think/wish that it should be possible to achieve this and I really would like to know how, as this is puzzling me for quite some time now...
[EDIT]
To perhaps clarify the motivation behind my question, I want to program an Exporter-class and let different, more specialized classes derive from it. While the top-level Exporter-class is generally only for cosmetic/structural purpose, a GraphExporter-class is derived, that should again serve as a base-class for even more specialzed export. However, similar to the Animal-example, I would like to be able to define GraphExporter* even on specialized/derived classes (e.g. on SpecialGraphExplorer) but when calling "write( out_file )", it should call the appropriate member function for SpecialGraphExporter instead of GraphExporter::write( out_file).
Maybe this makes my situation and intentions clearer.
Best,
Shadow
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
我不使用模板,但我认为:
(1)你不能在类中使用模板,模板更像是全局类型或全局变量。
(2) 在 OOP 中,您提出的相同问题以及您尝试使用模板解决的问题,可以通过使用继承来解决。
类的工作方式与模板类似,您可以通过添加新内容来扩展,或者用指针、指向对象的指针(也称为“引用”)和重写虚函数来替换类的内容。
干杯。
I don't work with templates, but I think:
(1) You cannot use templates inside a class, templates are more like global types or global variables.
(2) In O.O.P., the same problem you present, and that you are trying to solve by using templates, can be solved by using inheritance.
Classes work similar to templates, you can extended by adding new things, or replace things of classes with pointers, pointers to objects (A.K.A. "references") and overriding virtual functions.
Cheers.
经过一番思考,我认为这是经典的多方法要求,即一种基于多个参数的运行时类型进行调度的方法。相比之下,通常的虚拟函数是
单一调度
(它们仅在this
类型上调度)。请参阅以下内容:
以下是维基百科文章中的“简单”方法供参考(不太简单的方法对于大量派生类型的扩展效果更好):
After some thinking I recognized this as the classic multi-method requirement, i.e. a method that dispatches based on the runtime type of more than one parameter. Usual virtual functions are
single dispatch
in comparison (and they dispatch on the type ofthis
only).Refer to the following:
Here is the 'simple' approach from the wikipedia article for reference (the less simple approach scales better for larger number of derived types):
显然,虚拟成员函数模板是不允许的,理论上也是无法实现的。为了构建基类的虚拟表,需要有有限数量的虚拟函数指针条目。函数模板将允许无限量的“重载”(即实例化)。
从理论上讲,如果语言(如 C++)具有某种机制来指定实际(有限)实例化列表,则它可以允许虚拟成员函数模板。 C++ 确实有这种机制(即显式模板实例化),所以我想在较新的 C++ 标准中可以做到这一点(尽管我不知道编译器供应商实现此功能会带来什么麻烦)。但是,这只是理论上的讨论,在实践中,这是根本不允许的。事实仍然是,您必须使虚拟函数的数量有限(不允许使用模板)。
当然,这并不意味着类模板不能有虚函数,也不意味着虚函数不能调用函数模板。因此,有很多类似的解决方案(例如访客模式或其他方案)。
我认为,一个优雅地满足您的目的(尽管很难理解)的解决方案如下(基本上是访问者模式):
上面是一个简洁的解决方案,因为它允许您定义有限数量的重载只需在一处(在 Eater_impl 类模板中),而派生类中您所需要的只是一个函数模板(对于特殊情况,可能还需要额外的重载)。当然,有一点开销,但我想可以多花点心思来减少开销(额外的引用存储和 Eater_impl 的动态分配)。我猜想,奇怪的重复模板模式可能会以某种方式用于此目的。
Obviously, virtual member function templates are not allowed and could not be realized even theoretically. To build a base class' virtual table, there needs to be a finite number of virtual function-pointer entries. A function template would admit an indefinite amount of "overloads" (i.e. instantiations).
Theoretically-speaking, a language (like C++) could allow virtual member function templates if it had some mechanism to specify the actual (finite) list of instantiations. C++ does have that mechanism (i.e. explicit template instantiations), so I guess it could be possible to do this in a newer C++ standard (although I have no idea what trouble it would entail for compiler vendors to implement this feature). But, that's just a theoretical discussion, in practice, this is simply not allowed. The fact remains, you have to make the number of virtual functions finite (no templates allowed).
Of course, that doesn't mean that class template cannot have virtual functions, nor does it mean that virtual functions cannot call function templates. So, there are many solutions in that vein (like the Visitor pattern or other schemes).
One solution that, I think, serves your purpose (although it is hard to comprehend) elegantly is the following (which is basically a visitor pattern):
The above is a neat solution because it allows you to define a finite number of overloads that you want in one place only (in the Eater_impl class template) and all you need in the derived class is a function template (and possibly additional overloads, for special cases). There is, of course, a bit of overhead, but I guess that a bit more thought could be put into it to reduce the overhead (additional reference storage and dynamic allocation of Eater_impl). I guess, the curiously recurring template pattern could probably be employed somehow to this end.
我认为访客模式可以是一个解决方案。
更新
我完成了我的示例:
打印:
I think the visitor pattern can be a solution.
UPDATE
I finished my example:
this prints:
根据 Mikael 的帖子,我制作了另一个分支,使用 CRTP 并遵循 Eigen 的使用
categories()
进行显式子类引用的风格:输出:
此代码段可以在此处的源代码中找到:重现:c808ef0:cpp_quick/virtual_template.cc
Per Mikael's post, I have made another offshoot, using the CRTP and following Eigen's style of using
derived()
for an explicit subclass reference:Output:
This snippet may be found in source here: repro:c808ef0:cpp_quick/virtual_template.cc
不允许使用虚拟模板功能。不过,您可以在此处使用其中之一或另一个。
您可以使用虚拟方法创建一个界面,并通过饮食界面来实现各种动物。 (即 PIMPL)
不太人类的直觉是将非成员非朋友模板函数作为自由函数,它可以对任何动物进行模板化 const 引用并让它们相应地吃东西。
根据记录,您不需要此处的模板。基类上的纯虚拟抽象方法足以强制和连接所有动物必须吃的地方,并定义它们如何通过覆盖来做到这一点,提供常规虚拟就足以说明所有动物都可以吃,但如果它们没有特定方式,然后他们可以使用此默认方式。
Virtual template function is not allowed. However you can use one OR the other here.
You could make an interface using virtual methods and implement your various animals in terms of having an eating interface. (i.e. PIMPL)
Less human intuitive would be having a non-member non-friend template function as a free function which could take templated const reference to any animal and make them eat accordingly.
For the record you don't need templates here. Pure virtual abstract method on the base class is enough to force and interface where all animals must eat and define how they do so with an override, providing a regular virtual would be enough to say all animals can eat but if they don't have a specific way then they can use this default way.
您可以使用虚函数创建一个模板类,并在派生类中实现该函数,而不使用模板,如下所示:
不幸的是,我还没有找到一种方法来创建带有模板参数的虚函数,而不将该类声明为模板,并且它是派生类上的模板类型
You can create a template class with virtual function, and implement the function in the derived class without using template in the follwing way:
unfortunately i havn't found a way to create a virtual function with template parameters without declaring the class as a template and it template type on the dervied class
我已经复制了您的代码并对其进行了修改,所以现在它应该完全按照您的要求工作:
I have copied your code and modified it, so now it should work exactly as you want:
在您的场景中,您尝试将编译时多态性与运行时多态性混合在一起,但不能在这个“方向”上完成。
重要的是,您的 AMOUNT 模板参数表示要基于每个 eat 实现使用的所有操作的并集来实现的类型的预期接口。如果您在哪里创建一个抽象类型来声明每个操作,使它们在需要时成为虚拟的,那么您可以使用不同的类型(派生自 AMOUNT 接口)来调用 eat 。它会按预期运行。
In you scenario, you are trying to mix compile time polymorphism with runtime polymorphism, but it cannot be done in this "direction".
Essential, your AMOUNT template argument represents an expected interface for the type to implement based on the union of all the operations each implementation of eat uses. If you where to create an abstract type that declared each of those operations making them virtual where needed, then you could call eat with different types (that derived from your AMOUNT interface). And it would behave as expected.