用CRTP +可变参数模板模拟动态多态性

时间:2016-09-25 19:18:28

标签: c++ boost polymorphism tuples crtp

我需要用相当大的类替换代码中的动态多态,并且我遇到了CRTP +可变参数模板设计的问题。

为了说明,我在下面提供了玩具问题。

有一个动态多态类,以便使用以下接口:

int main() { 
  Loop loop;
  Add add ;
  loop.set(add);
  loop.run();
  Sub sub ;
  loop.set(sub); 
}

当然,SubAdd派生自基类Operationop(double & v)是虚拟方法。

由于其他原因,我需要保留此接口,同时通过静态多态更改动态,这是我的解决方案,直到现在:

template <typename T> class Operation { 
public: 
  double d=1.;
  void op(double & d) { static_cast<T*>(this)->op(d);}
};
class Add final: public Operation<Add> { 
public: void op(double & v) { v += d; }
};
class Sub final: public Operation<Sub> {
public: void op(double & v) { v -= d; }
};

Loop类:

template <typename... Types>
class MetaLoop {
public:
  double d = 0 ;
  template <typename T> void set(T & t) { 
  std_cxx17::get<std::vector<T*> >(data).push_back(&t) ; 
  }
  template <typename T> void run() { 
    for (auto j = 0; j<2E9 ; ++j)
      std_cxx17::get<std::vector<T*> >(data).back()->op(d);
    std::cout << d << std::endl; 
  }
private:
  std::tuple<std::vector<Types*>... > data ;
};
typedef MetaLoop<Add,Sub> Loop ;

std_cxx17::get<T*>(data)是对按类型选择元组中元素的方法的调用,类似于this post

然而,这个解决方案需要几乎我想要的接口,除了需要向run()提供模板参数,即run<Add>run<Sub>以及需要在Add

注册 SubMetaLooptypedef MetaLoop<Add,Sub> Loop

我想知道是否可以在不使用宏的情况下:

  1. 自动将Operation<T>课程注册到MetaLoop(即自动拨打typedef Metaloop<...operations...>
  2. 避免将模板类提供给run方法。 (即拨打run()而不是run<Add>()
  3. 从库中获取此方法:是否存在可以静态提供此类多态性的库(例如Boost)?
  4. 请记住,我知道我可以使用其他几种架构,但我还有许多其他限制。这只是一个玩具问题,可以举例说明。

    提前致谢!

    编辑1:

    问题2已经回答了!见这里:http://coliru.stacked-crooked.com/a/e68ea7faabba3e40

    感谢https://www.reddit.com/user/17b29a

1 个答案:

答案 0 :(得分:0)

您可以键入 - 删除您的类型,如下例所示:

#include <iostream>
#include <vector>
#include <utility>

template<typename T>
struct Operation {
    void op(double &d) { static_cast<T*>(this)->op(d) ; }
    double d{1};
};

struct Add: Operation<Add> {
    void op(double &v) { v += d; }
};

struct Sub: Operation<Sub> {
    void op(double &v) { v -= d; }
};

class Loop {
    using Fn = void(*)(double &, void *);

    template<typename T>
    static void op(double &d, void *op) {
        static_cast<T*>(op)->op(d);
    }

public:
    template<typename T>
    void set(T &t) {
        data.emplace_back(&op<T>, &t);
    }

    void run() {
         auto &p = data.back();
         for(auto j = 0; j < 10; ++j) {
             p.first(d, p.second);
         }

         std::cout << d << std::endl;
    }

private:
    std::vector<std::pair<Fn, void*>> data;
    double d = 0;
};

int main() {
    Loop loop;
    Add add;
    loop.set(add);
    loop.run();
    Sub sub;
    loop.set(sub);
    // ...
}

另请注意,在这种情况下,CRTP惯用法是无用的,您可以删除模板类Operation
只需定义AddSub,如下所示:

struct Add {
    void op(double &v) { v += d; }
    double d{1};
};

struct Sub {
    void op(double &v) { v -= d; }
    double d{1};
};

如果您想区分AddSub并提供不同的算法,可以使用专业化。
举个例子:

class Loop {
    using Fn = void(*)(double &, void *);

    template<typename T>
    static void op(double &d, void *op);

public:
    template<typename T>
    void set(T &t) {
        data.emplace_back(&op<T>, &t);
    }

    void run() {
        auto &p = data.back();
        for(auto j = 0; j < 10; ++j) {
            p.first(d, p.second);
        }

        std::cout << d << std::endl;
    }

private:
    std::vector<std::pair<Fn, void*>> data;
    double d = 0;
};

template<>
void Loop::op<Add>(double &d, void *op) {
    Add *add = static_cast<Add*>(op);
    // do whatever you want with add
    add->op(d);
}

template<>
void Loop::op<Sub>(double &d, void *op) {
    Sub *sub = static_cast<Sub*>(op);
    // do whatever you want with sub
    sub->op(d);
}

请注意,该解决方案符合问题中列出的所有要求,并且与OP提供的main一起使用。