c ++中的虚拟模板函数

时间:2017-08-23 21:15:18

标签: c++ function templates polymorphism virtual

我一直在寻找一种同时使用模板和多态的方法。这是我的问题的简化版本:

#include <iostream>
#include <vector>
using std::cout;
using std::endl;

//*******************************************************************
//*******************************************************************

struct DerivedStuff1
{
    static void eval() { cout << "evaluating DerivedStuff1" << endl; }
};

struct DerivedStuff2
{
    static void eval() { cout << "evaluating DerivedStuff2" << endl; }
};

//*******************************************************************
//*******************************************************************
class BaseClass
{
public:
    template<typename StuffType> virtual void eval() const = 0;
};

class DerivedClass1 : public BaseClass
{
public:
    template<typename StuffType> virtual void eval() const
    {
        std::cout << "We are in DerivedClass1: ";
        StuffType::eval();
    }
};

class DerivedClass2 : public BaseClass
{
public:
    template<typename StuffType> virtual void eval() const
    {
        std::cout << "We are in DerivedClass2: ";
        StuffType::eval();
    }
};

int main()
{
    BaseClass* c1 = new DerivedClass1;
    c1->eval<DerivedStuff1>();
    c1->eval<DerivedStuff2>();

    BaseClass* c2 = new DerivedClass2;
    c2->eval<DerivedStuff1>();
    c2->eval<DerivedStuff2>();

    return 0;
}

此代码无法编译,因为C ++中不允许使用虚拟模板函数。我找到了一些解决这个问题的方法(CRTP等),但没有一个真正令人满意。有没有优雅的方法来解决这个问题?

4 个答案:

答案 0 :(得分:2)

visitor pattern改变了运行时多态性,使运行时多态函数模板成为可能。除了模板化之外,它还有其他合法的用途,所以我想你可以称之为优雅。

您的示例可能如下所示:

lessc styles.less

Demo

当然,访客的所有缺点都适用于此。

答案 1 :(得分:1)

由于C ++中的虚拟模板方法不允许,您可以创建一个类模板并调用类模板参数的静态函数。

#include <iostream>
#include <vector>
using std::cout;
using std::endl;

//*******************************************************************
//*******************************************************************

struct DerivedStuff1
{
    static void eval() { cout << "evaluating DerivedStuff1" << endl; }
};

struct DerivedStuff2
{
    static void eval() { cout << "evaluating DerivedStuff2" << endl; }
};

//*******************************************************************
//*******************************************************************
class BaseClass
{
public:
    virtual void eval() const = 0;
};

template<typename StuffType>
class DerivedClass1 : public BaseClass
{
public:
    virtual void eval() const
    {
        std::cout << "We are in DerivedClass1: ";
        StuffType::eval();
    }
};

template<typename StuffType>
class DerivedClass2 : public BaseClass
{
public:
    virtual void eval() const
    {
        std::cout << "We are in DerivedClass2: ";
        StuffType::eval();
    }
};

int main()
{
    BaseClass* c1 = new DerivedClass1<DerivedStuff1>;
    c1->eval();
    c1 = new DerivedClass1<DerivedStuff2>;
    c1->eval();

    BaseClass* c2 = new DerivedClass2<DerivedStuff1>;
    c2->eval();
    c2 = new DerivedClass2<DerivedStuff2>;
    c2->eval();

    // deletes

    return 0;
}

输出

We are in DerivedClass1: evaluating DerivedStuff1
We are in DerivedClass1: evaluating DerivedStuff2
We are in DerivedClass2: evaluating DerivedStuff1
We are in DerivedClass2: evaluating DerivedStuff2

答案 2 :(得分:1)

您可以重新发明vtable并在运行时解析函数指针。但是,您必须明确地在派生类上实例化模板,但我没有看到任何不需要的方法。

快速而肮脏的例子:

#include <map>
#include <iostream>

class Base {
public:
  typedef void (Base::*eval_ptr)();
  using eval_vtable = std::map<std::type_index, eval_ptr>;

  Base(eval_vtable const& eval_p) : eval_ptrs(eval_p) {}

  template<typename T>
  void eval() {
    auto handler = eval_ptrs.find(type_index(typeid(T)));
    if(handler != eval_ptrs.end()) {
      auto handler_ptr = handler->second;
      (this->*handler_ptr)();
    }
  }

  eval_vtable const& eval_ptrs;
};

class Derived : public Base {
public:
    Derived()
     : Base(eval_functions) {}

  template<typename T>
  void eval_impl() {
    std::cout << typeid(T).name() << "\n";
  }

  static eval_vtable eval_functions;
};

Base::eval_vtable Derived::eval_functions = {
  { type_index(typeid(int)), eval_ptr(&Derived::eval_impl<int>) },
  { type_index(typeid(float)), eval_ptr(&Derived::eval_impl<float>) },
  { type_index(typeid(short)), eval_ptr(&Derived::eval_impl<short>) },
};

int main(int argc, const char* argv[]) {
  Derived x;
  Base * x_as_base = &x;

  x_as_base->eval<int>(); // calls Derived::eval_impl<int>()
  return 0;
}

这不会完全,但它会为你提供我能想到的模板化虚拟函数最接近的东西。

编辑:对于记录,我不主张任何人使用此功能。我宁愿重新考虑设计,以避免首先在这个特定的角落里画画。请将我的答案视为理论问题的学术解决方案,而不是实际的工程建议。

答案 3 :(得分:1)

您不能混合模板(编译时)和多态(运行时)。就是这样。

因此,一个可行的解决方法是删除模板。例如,它可能需要一个函数指针或更多的多态:

//*******************************************************************
//*******************************************************************

struct InterfaceStuff{
  virtual void eval() = 0;
}

struct DerivedStuff1 : public InterfaceStuff
{
    void eval() { cout << "evaluating DerivedStuff1" << endl; }
};

struct DerivedStuff2 : public InterfaceStuff
{
    void eval() { cout << "evaluating DerivedStuff2" << endl; }
};

//*******************************************************************
//*******************************************************************
class BaseClass
{
public:
    virtual void eval(InterfaceStuff* interface) const = 0;
};

class DerivedClass1 : public BaseClass
{
public:
    virtual void eval(InterfaceStuff* interface) const
    {
        std::cout << "We are in DerivedClass1: ";
        interface->eval();
    }
};

class DerivedClass2 : public BaseClass
{
public:
    virtual void eval(InterfaceStuff* interface) const
    {
        std::cout << "We are in DerivedClass2: ";
        interface->eval();
    }
};

另一个可行的解决方法是删除多态,只需使用更多模板:

struct DerivedStuff1
{
    static void eval() { cout << "evaluating DerivedStuff1" << endl; }
};

struct DerivedStuff2
{
    static void eval() { cout << "evaluating DerivedStuff2" << endl; }
};

//*******************************************************************
//*******************************************************************
class BaseClass
{
public:
    template<typename Eval,typename StuffType> void eval() const
    {
        Eval::eval();
        StuffType::eval();
    }
};

class DerivedClass1 : public BaseClass
{
};

class DerivedClass2 : public BaseClass
{
};

另一种方式,你必须选择一种。