如何实现“虚拟模板功能"在 C++ 中

2021-12-13 00:00:00 templates c++ virtual

首先:我已经阅读过并且现在我知道虚拟模板成员函数(还?)在 C++ 中是不可能的.一种解决方法是使类成为模板,然后也在成员函数中使用模板参数.

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.

但是在 OOP 的上下文中,如果类实际上是模板,我发现下面的示例不会很自然".请注意,代码实际上不起作用,但 gcc-4.3.4 报告:error: templates may not ‘virtual’

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;
}

所以创建一个Fish foo"有点奇怪.然而,我似乎希望为每只动物提供任意数量的食物.

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 );

这在查看 for 循环时变得特别有用.一个人可能想给所有不同的动物喂食特定量(FoodAmount)(例如通过eat() 和bind1st()),这不容易做到,尽管我觉得这非常直观(因此在某种程度上自然.虽然现在有些人可能想争辩说这是由于向量的统一"特征,我认为/希望应该有可能实现这一点,我真的很想知道如何,因为这是纠结了好久...

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...

为了澄清我的问题背后的动机,我想编写一个 Exporter 类,并让不同的、更专业的类从中派生.虽然顶级 Exporter-class 通常仅用于装饰/结构目的,但 GraphExporter-class 是派生的,它应该再次作为更专业导出的基类.但是,与 Animal-example 类似,我希望即使在专用/派生类(例如在 SpecialGraphExplorer 上)也能够定义 GraphExporter*,但是在调用write(out_file)"时,它应该为 SpecialGraphExporter 调用适当的成员函数GraphExporter::write(out_file).

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.

最好,

阴影

推荐答案

经过一番思考,我认识到这是经典的多方法要求,即基于运行时调度的方法多个参数的类型.相比之下,通常的虚函数是 single dispatch(并且它们仅在 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 of this only).

参考以下内容:

  • Andrei Alexandrescu 撰写了有关在现代 C++ 设计"中使用泛型实现多方法的文章(C++ 的开创性部分?)
    • 第 11 章:多方法" - 它实现了基本的多方法,使它们成为对数(使用有序类型列表),然后一直到恒定时间多方法.相当强大的东西!
    • Andrei Alexandrescu has written (the seminal bits for C++?) on implementing multi-methods using generics in 'Modern C++ design'
      • Chapter 11: "Multimethods" - it implements basic multi-methods, making them logarithmic (using ordered typelists) and then going all the way to constant-time multi-methods. Quite powerful stuff !
      • 不使用任何类型的类型转换(动态、静态、重新解释、const 或 C 风格)
      • 不使用RTTI;
      • 不使用预处理器;
      • 强类型安全;
      • 单独编译;
      • 多方法执行的恒定时间;
      • 在多方法调用期间没有动态内存分配(通过 new 或 malloc);
      • 不使用非标准库;
      • 仅使用标准 C++ 功能.

      这是维基百科文章中的简单"方法以供参考(对于大量派生类型,越不简单的方法扩展性越好):

      Here is the 'simple' approach from the wikipedia article for reference (the less simple approach scales better for larger number of derived types):

      // Example using run time type comparison via dynamic_cast
      
      struct Thing {
          virtual void collideWith(Thing& other) = 0;
      }
      
      struct Asteroid : Thing {
          void collideWith(Thing& other) {
              // dynamic_cast to a pointer type returns NULL if the cast fails
              // (dynamic_cast to a reference type would throw an exception on failure)
              if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
                  // handle Asteroid-Asteroid collision
              } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
                  // handle Asteroid-Spaceship collision
              } else {
                  // default collision handling here
              }
          }
      }
      
      struct Spaceship : Thing {
          void collideWith(Thing& other) {
              if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
                  // handle Spaceship-Asteroid collision
              } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
                  // handle Spaceship-Spaceship collision
              } else {
                  // default collision handling here
              }
          }
      }
      

相关文章