跟我学c++中级篇——手动实现类tuple

本文详细描述了一个自定义类Tuple的实现过程,通过模板和变参类来创建类指针,并展示了如何获取并操作内部对象,尽管最初设想采用多态和静态容器,但因模板匹配问题最终选择了动态生成和索引控制的方案,留待后续优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、问题

在某个场景下需要实现一个类似于Tuple的类,主要是用于类指针的创建。直接使用Tuple其实是可以的,但一开始没有想清楚,所以后来绕了一圈儿后还是回到了类Tuple的实现。下面把这个实现的过程简要说明分析一下。

二、实现过程

实现的过程其实并不复杂,利用一个变参模板类展开即可:

template <typename... T> class CmdObj;
template <> class CmdObj<> {};
template <typename T, typename... N> class CmdObj<T, N...> : public CmdObj<N...> {
public:
  using Base = CmdObj<N...>;
  CmdObj() : Base() {
    T *ptr = new T();
    if (ptr != nullptr) {
      m_t = ptr;
    }
  }
  ~CmdObj() = default;

public:
  Base *getBase() { return this; }

public:
  T *m_t;
};

代码很简单,其实就是一个变量模板类的实现。

三、获取对象和分析

在创建完成对象指针后,就可以获取其内部的指针了:

template <int id, typename T> class CmdObjElement {
public:
  using Type = typename CmdObjElement<id - 1, typename T::Base>::Type;
};
template <typename T> class CmdObjElement<0, T> {
public:
  using Type = T;
};
template <int id, typename T> constexpr auto &GetCmdObj(T &t) {
  using Type = typename CmdObjElement<id, T>::Type;
  return static_cast<Type &>(t).m_t;
}

将上面的两部分代码合并在一起,写一个例程,然后编译:

#include <iostream>
#include <memory>
#include <tuple>
#include <type_traits>

template<typename ... T>
class CmdObj;
/* First instantiated from: insights.cpp:11 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObj<B, C, D> : public CmdObj<C, D>
{
  
  public: 
  using Base = CmdObj<C, D>;
  inline CmdObj()
  : CmdObj<C, D>()
  {
    B * ptr = new B();
    if(ptr != nullptr) {
      this->m_t = ptr;
    } 
    
  }
  
  inline ~CmdObj() noexcept = default;
  
  public: 
  inline CmdObj<C, D> * getBase();
  
  
  public: 
  B * m_t;
};

#endif
/* First instantiated from: insights.cpp:11 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObj<C, D> : public CmdObj<D>
{
  
  public: 
  using Base = CmdObj<D>;
  inline CmdObj()
  : CmdObj<D>()
  {
    C * ptr = new C();
    if(ptr != nullptr) {
      this->m_t = ptr;
    } 
    
  }
  
  inline ~CmdObj() noexcept = default;
  
  public: 
  inline CmdObj<D> * getBase();
  
  
  public: 
  C * m_t;
};

#endif
/* First instantiated from: insights.cpp:11 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObj<D> : public CmdObj<>
{
  
  public: 
  using Base = CmdObj<>;
  inline CmdObj()
  : CmdObj<>()
  {
    D * ptr = new D();
    if(ptr != nullptr) {
      this->m_t = ptr;
    } 
    
  }
  
  inline ~CmdObj() noexcept = default;
  
  public: 
  inline CmdObj<> * getBase();
  
  
  public: 
  D * m_t;
};

#endif
/* First instantiated from: insights.cpp:57 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObj<A, B, C, D> : public CmdObj<B, C, D>
{
  
  public: 
  using Base = CmdObj<B, C, D>;
  inline CmdObj()
  : CmdObj<B, C, D>()
  {
    A * ptr = new A();
    if(ptr != nullptr) {
      this->m_t = ptr;
    } 
    
  }
  
  inline ~CmdObj() noexcept = default;
  
  public: 
  inline CmdObj<B, C, D> * getBase();
  
  
  public: 
  A * m_t;
};

#endif

template<>
class CmdObj<>
{
  public: 
};


template<typename T, typename ... N>
class CmdObj<T, N...> : public CmdObj<N...>
{
  
  public: 
  using Base = CmdObj<N...>;
  inline CmdObj()
  : Base{}
  {
    T * ptr = new T{};
    if(ptr != nullptr) {
      this->m_t = ptr;
    } 
    
  }
  
  inline ~CmdObj() = default;
  
  public: 
  inline Base * getBase()
  {
    return this;
  }
  
  
  public: 
  T * m_t;
};


template<int id, typename T>
class CmdObjElement
{
  
  public: 
  using Type = typename CmdObjElement<id - 1, typename T::Base>::Type;
};

/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<0, CmdObj<B, C, D> >
{
  
  public: 
  using Type = CmdObj<B, C, D>;
};

#endif
/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<1, CmdObj<B, C, D> >
{
  
  public: 
  using Type = CmdObjElement<0, CmdObj<C, D> >::CmdObj<C, D>;
};

#endif
/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<0, CmdObj<C, D> >
{
  
  public: 
  using Type = CmdObj<C, D>;
};

#endif
/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<2, CmdObj<B, C, D> >
{
  
  public: 
  using Type = CmdObjElement<0, CmdObj<D> >::CmdObj<D>;
};

#endif
/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<1, CmdObj<C, D> >
{
  
  public: 
  using Type = CmdObjElement<0, CmdObj<D> >::CmdObj<D>;
};

#endif
/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<0, CmdObj<D> >
{
  
  public: 
  using Type = CmdObj<D>;
};

#endif
/* First instantiated from: insights.cpp:37 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<0, CmdObj<A, B, C, D> >
{
  
  public: 
  using Type = CmdObj<A, B, C, D>;
};

#endif
/* First instantiated from: insights.cpp:37 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<1, CmdObj<A, B, C, D> >
{
  
  public: 
  using Type = CmdObjElement<0, CmdObj<B, C, D> >::CmdObj<B, C, D>;
};

#endif
/* First instantiated from: insights.cpp:37 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<2, CmdObj<A, B, C, D> >
{
  
  public: 
  using Type = CmdObjElement<0, CmdObj<C, D> >::CmdObj<C, D>;
};

#endif
/* First instantiated from: insights.cpp:37 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<3, CmdObj<A, B, C, D> >
{
  
  public: 
  using Type = CmdObjElement<0, CmdObj<D> >::CmdObj<D>;
};

#endif

template<typename T>
class CmdObjElement<0, T>
{
  
  public: 
  using Type = T;
};


template<int id, typename T>
inline constexpr auto & GetCmdObj(T & t)
{
  using Type = typename CmdObjElement<id, T>::Type;
  return static_cast<Type &>(t).m_t;
}


#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr A *& GetCmdObj<0, CmdObj<A, B, C, D> >(CmdObj<A, B, C, D> & t)
{
  using Type = CmdObjElement<0, CmdObj<A, B, C, D> >::CmdObj<A, B, C, D>;
  return static_cast<CmdObjElement<0, CmdObj<A, B, C, D> >::CmdObj<A, B, C, D> &>(t).m_t;
}
#endif


#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr B *& GetCmdObj<1, CmdObj<A, B, C, D> >(CmdObj<A, B, C, D> & t)
{
  using Type = CmdObjElement<0, CmdObj<B, C, D> >::CmdObj<B, C, D>;
  return static_cast<CmdObjElement<0, CmdObj<B, C, D> >::CmdObj<B, C, D> &>(t).m_t;
}
#endif


#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr C *& GetCmdObj<2, CmdObj<A, B, C, D> >(CmdObj<A, B, C, D> & t)
{
  using Type = CmdObjElement<0, CmdObj<C, D> >::CmdObj<C, D>;
  return static_cast<CmdObjElement<0, CmdObj<C, D> >::CmdObj<C, D> &>(t).m_t;
}
#endif


#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr D *& GetCmdObj<3, CmdObj<A, B, C, D> >(CmdObj<A, B, C, D> & t)
{
  using Type = CmdObjElement<0, CmdObj<D> >::CmdObj<D>;
  return static_cast<CmdObjElement<0, CmdObj<D> >::CmdObj<D> &>(t).m_t;
}
#endif



struct A
{
  inline void test()
  {
    std::operator<<(std::cout, "this is A!").operator<<(std::endl);
  }
  
};


struct B
{
  inline void test()
  {
    std::operator<<(std::cout, "this is b!").operator<<(std::endl);
  }
  
};


struct C
{
  inline void test()
  {
    std::operator<<(std::cout, "this is c!").operator<<(std::endl);
  }
  
};


struct D
{
  inline void test()
  {
    std::operator<<(std::cout, "this is d!").operator<<(std::endl);
  }
  
};


void Display()
{
  std::shared_ptr<CmdObj<A, B, C, D> > x = std::make_shared<CmdObj<A, B, C, D> >();
  A * xA = GetCmdObj<0>(static_cast<const std::__shared_ptr_access<CmdObj<A, B, C, D>, 2, false, false>&>(x).operator*());
  xA->test();
  B * xB = GetCmdObj<1>(static_cast<const std::__shared_ptr_access<CmdObj<A, B, C, D>, 2, false, false>&>(x).operator*());
  xB->test();
  C * xC = GetCmdObj<2>(static_cast<const std::__shared_ptr_access<CmdObj<A, B, C, D>, 2, false, false>&>(x).operator*());
  xC->test();
  D * xD = GetCmdObj<3>(static_cast<const std::__shared_ptr_access<CmdObj<A, B, C, D>, 2, false, false>&>(x).operator*());
  xD->test();
}

int main()
{
  Display();
  return 0;
}

有一个好工具就是好用啊,分析代码也方便太多了。有了编译前后的代码比较,再结合模板的相关知识,应该是一目了然了。

四、总结

到最后的实现仍然是使用了类似std::tuple的实现,通过索引ID来获取不同类型的具体的指针。其实最初是想的通过一个基类形成的静态容器返回然后通过多态来达到对不同类型的控制。可惜的是模板的匹配总是无法通过,只好改成一个动态生成动态获取的过程。后来又想通过在遍历的时候儿拿到对象指针而非固定写到指定的索引上,毕竟这样就无法修改对象生成的顺序了。可又无法解决不同对象回传的问题(通过基类返回直接就崩溃了)。故而目前只能还是先使用这种索引控制来得到具体的对象指针,以后有机会再优化它。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值