【问题标题】:Variadic template class iterate over types可变参数模板类迭代类型
【发布时间】:2019-02-24 02:05:14
【问题描述】:

我正在尝试创建一个可变参数模板类,其方法需要遍历模板参数并为每种类型调用一个不相关的模板函数。

这个想法是用特定的模板参数扩展这个模板类,以便封装基本行为。

一般想法的一个简单例子是这个(可怕的)Checker 类:

class Arg {
    Arg() {};
    virtual ~Arg() = default;
};

class A : public Arg{ /* ... */ };
class B : public Arg{ /* ... */ };
class C : public Arg{ /* ... */ };

template <typename ... SubClasses>
class Checker {
public:

    Checker() {};
    virtual ~Checker() = default;

    bool isOneOf(Arg* base) const;
};


template <typename ... SubClasses>
bool Checker<SubClasses>::isOneOf(Arg* arg) const
{
    // NOTE: I don't know how I would iterate over the class types....
    for (auto SubClass : SubClasses...) { // <-- not valid code
        if (std::dynamic_cast<SubClass>(arg)) {
            return true;
        }
    }
    return false;
}

class Whatever : public Checker<A, B> {};

auto whatever = new Whatever();
auto a = new A();
auto b = new B();
auto c = new C();

whatever->isOneOf(a); // true
whatever->isOneOf(b); // true
whatever->isOneOf(c); // false

为了让isOneOf 工作,它需要能够迭代模板参数

我正在使用 C++14 编译器,但我无法使用boost。

【问题讨论】:

    标签: c++ templates variadic-templates


    【解决方案1】:

    我认为可能的 C++14 解决方案(也包括 C++11)是

    template <typename ... SubClasses>
    bool Checker<SubClasses...>::isOneOf (Arg * arg) const
     {
       using unused = bool[];
    
       bool ret { false };
    
       (void)unused { false, ret |= (nullptr != dynamic_cast<SubClasses *>(arg))... };
    
       return ret;
     }
    

    不幸的是,没有拍摄电路(即:所有nullptr != dynamic_cast 测试在第一个为真时也被评估(如果编译器没有优化它)。

    如果你可以使用 C++17,那么模板折叠就更简单了

    template <typename ... SubClasses>
    bool Checker<SubClasses...>::isOneOf (Arg * arg) const
     { return (... || (nullptr != dynamic_cast<SubClasses *>(arg)) ); }
    

    【讨论】:

    • ret=ret|| 短路 11
    【解决方案2】:
    template<class T>struct tag_t{using type=T;};
    template<class Tag>using type_t=typename Tag::type;
    
    template<class F>
    void foreach_arg(F&&f){
      return [f=std::forward<F>(f)](auto&&...args){
        int discard[]={0,(void(
          f(decltype(args)(args)
        ),0)...};
       (void)discard;
      };
    }
    

    现在:

    bool isOneOf(Arg* base) const{
      bool r=false;
      foreach_arg( [&](auto tag){ r = r || dynamic_cast<type_t<decltype(tag)>>( base ); } )
      ( tag_t<SubClasses>{}... );
      return r;
    }
    

    【讨论】:

      【解决方案3】:

      使用 C++17,应该可以修改折叠表达式并一次性完成。但是,对于 C++14,如果不是最简洁的话,可能是最易读的方法是使用帮助模板类和一些专业化......

      #include <iostream>
      
      class Arg {
      
      public:
          Arg() {};
          virtual ~Arg() = default;
      };
      
      template<typename ...Args> struct is_one_of_helper;
      
      template<>
      struct is_one_of_helper<> {
      
          static bool isOneOf(Arg *arg)
          {
              return false;
          }
      };
      
      template<typename A, typename ...Args>
      struct is_one_of_helper<A, Args...> {
      
          static bool isOneOf(Arg *arg)
          {
              if (dynamic_cast<A *>(arg))
                  return true;
      
              return is_one_of_helper<Args...>::isOneOf(arg);
          }
      };
      
      template <typename ... SubClasses>
      class Checker {
      public:
      
          Checker() {};
          virtual ~Checker() = default;
      
          bool isOneOf(Arg* base) const
          {
              return is_one_of_helper<SubClasses...>::isOneOf(base);
          }
      };
      
      class A : public Arg{ /* ... */ };
      class B : public Arg{ /* ... */ };
      class C : public Arg{ /* ... */ };
      
      int main()
      {
          Arg *a=new A;
          Arg *b=new B;
          Arg *c=new C;
      
          Checker<B, C> checker;
      
          std::cout << checker.isOneOf(a)
                << std::endl;
      
          std::cout << checker.isOneOf(b)
                << std::endl;
      
          std::cout << checker.isOneOf(c)
                << std::endl;
      }
      

      结果输出:

      0
      1
      1
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多