【问题标题】:accessing members of a templated class with variable numbers of templated arguments使用可变数量的模板化参数访问模板化类的成员
【发布时间】:2014-06-22 10:15:31
【问题描述】:

我有一个带有可变数量的模板参数的模板类。在这些情况下(我买不起 C++11),一个好的做法是创建一个我们称之为 none 的默认类,并将其设置为默认类,如下所示。

struct none {};

template<class T1=none, T2=none, T3=none>
class A{

   template<class T>
   double extract() { return none();}

   template<>
   double extract<T1>() { return m1_();}

   template<>
   double extract<T2>() { return m2_();}

   template<>
   double extract<T3> () { return m3_();}

   T1 m1_;
   T2 m2_;
   T3 m3_;
};

在这个阶段,我不知道如何实现可以访问每个模板化参数的通用/模板化访问器函数。

所有的模板化参数都不同,所以我专门为每个模板化参数使用 A::extract()。

有没有更好的方法来做到这一点?我可以查看任何类型的标记吗?

【问题讨论】:

  • 如果T1和T2是同一个类型,那么这种访问方式就变得不明确了。
  • 这将从is_same 中受益良多..
  • 这还能用吗?您不能将显式特化放在类范围内。
  • @n.m.我相信这只是伪代码。同样为通用案例调用 extract() 失败(返回值)

标签: c++ templates template-argument-deduction


【解决方案1】:
struct none {};

template <class T, class N>
class Holder : public N
{
  protected:
    T m;
    typedef Holder<T, N> First;
    double extractP(T*) { return m(); }
    template <class X> double extractP(X*) {
      return this->N::extractP(static_cast<X*>(0));
    }
};

template <class T>
class Holder<T, none>
{
  protected:
    T m;
    typedef Holder<T, none> First;
    double extractP(T*) { return m(); }
    template <class X> none extractP(X*) {
      return none();
    }
};



template <class T1 = none, class T2 = none, class T3 = none>
class A : Holder<T1, Holder<T2, Holder<T3, none> > >
{
  public:
    template <class T> double extract() {
      return this->extractP(static_cast<T*>(0));
    }
};

【讨论】:

    【解决方案2】:

    与 n.m 同名的解决方案,但更多的是 Boost 的 Variant 类设计。

    建议使用Variant 容器(对象的通用容器)并直接在它们上使用访问器。

    #include <iostream>
    #include <stdexcept>
    using namespace std;
    
    
    class BaseHolder
    {
    public:
      virtual ~BaseHolder(){}
      virtual BaseHolder* clone() const = 0;
    };
    
    template<typename T>
    class HoldData : public BaseHolder
    {
    public:
      HoldData(const T& t_) : t(t_){}
    
      virtual BaseHolder* clone() const {
        return new HoldData<T>(t);
      }
    
      T getData() {
          return t;
      }
    
    private:
      T t;
    };
    
    
    class Variant
    {
    public:
    
      Variant() : data(0) {}
    
      template<typename T>
      Variant(const T& t) : data(new HoldData<T>(t)){}
    
      Variant(const Variant& other) : data(other.data ? other.data->clone() : 0) {}
      ~Variant(){delete data;}
    
      template<typename T>
      T getData() {
          return ((HoldData<T>*)data)->getData();
      }
    
    private:
      BaseHolder* data;
    
    private:
      Variant& operator=(const Variant& other) { return *this;} // Not allowed
    };
    
    struct none {};
    
    class Container{
    public:
        Container() : m1_(0), m2_(0), m3_(0){}
        ~Container() {
            if(m1_)
                delete m1_;
            if(m2_)
                delete m1_;
            if(m3_)
                delete m1_;
        }
    
        none extract() { return none();}
    
        template<typename T>
        void insertM1(T obj) {
            m1_ = new Variant(obj);
        }
    
        template<typename T>
        T extractM1() {
            if(m1_ != 0)
                return m1_->getData<T>();
            else
                throw std::runtime_error("Element not set");
        }
    
        // TODO: implement m2 and m3
    
        Variant *m1_;
        Variant *m2_;
        Variant *m3_;
    };
    
    int main() {
    
        Container obj;
    
        char M1 = 'Z';
        obj.insertM1(M1);
    
        char extractedM1 = obj.extractM1<char>();
        cout << extractedM1;
    
        return 0;
    }
    

    http://ideone.com/BaCWSV

    【讨论】:

      【解决方案3】:

      您的课程似乎模仿了std::tuple,不幸的是,它是在 C++11 中添加的。好消息是您可以改用boost::tuple。

      作为使用示例:

      boost::tuple<std::string, double> t = boost::make_tuple("John Doe", 4.815162342);
      std::cout << boost::get<0>(t) << '\n';
      std::cout << boost::get<1>(t) << '\n';
      

      Live demo

      【讨论】:

      • 这是按索引访问,而不是按类型访问。
      • @MatthieuM。该类型在编译时是固定的,因此它也可以按类型索引。
      • 这不是它是否有效的问题,而是您希望向用户公开的界面的问题。如果您公开索引,那么他们必须始终记住映射索引 类型,这有点笨拙。
      • @MatthieuM。我不知道你在说什么接口,因为这里暴露的唯一接口是boost::tuple,你肯定不会去改变那个。
      • @Jefffrey:他 [非常正确] 指出 OP 希望按类型而不是按索引访问,并且您对此的回应没有考虑到必须执行类型的笨拙索引映射自己在你的脑海中。
      【解决方案4】:

      不访问 C++11 有点丑,但是你可以利用 Boost.Tuple:

      #include <iostream>
      #include <boost/tuple/tuple.hpp>
      
      template <size_t I, typename T, typename U>
      struct AccessImpl;
      
      template <size_t I, typename T, typename U>
      struct AccessImpl {
          template <typename Tuple>
          static T& impl(Tuple& tuple) {
              typedef typename ::boost::tuples::element<I+1, Tuple>::type Next;
              return AccessImpl<I+1, T, Next>::impl(tuple);
          }
      };
      
      template <size_t I, typename T>
      struct AccessImpl<I, T, T> {
          template <typename Tuple>
          static T& impl(Tuple& tuple) { return boost::get<I>(tuple); }
      };
      
      template <typename T, typename Tuple>
      T& access(Tuple& tuple) {
          typedef typename ::boost::tuples::element<0, Tuple>::type Head;
          return AccessImpl<0, T, Head>::impl(tuple);
      }
      
      int main() {
          boost::tuples::tuple<char, int, std::string> example('a', 1, "Hello, World!");
          std::cout << access<std::string>(example) << "\n";
          return 0;
      }
      

      正如预期的那样,prints "Hello, World!"。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多