CRTP 容器

发布于 2024-12-02 01:36:52 字数 993 浏览 2 评论 0原文

我正在尝试一些模板编程,对此我很陌生。我想要实现的是一些包含 STL 容器的 CRTP 类。让 class A{}; 作为(编译时)基类的示例,其中 class B{};class C{}; 在编译时按照 CRTP 样式“派生”。

现在 BC 都将包含容器。出于示例的目的,将其分别设为 std::vectorstd::set。现在,我想通过 begin() 和一个公开前向迭代器的 end() 函数来公开这些迭代器。但是,我不想公开 BC 内部的确切容器是什么,我想为 A 定义这些函数,以便在调用时使用 BC 的正确值。

这可能吗?现在我的计划是为 BC 建立一个 Iterator 内部类,其中将包含(一个向量或一个根据情况设置)并将调用委托给它。然而,这似乎是很多重复的粘合代码,我怀疑有更好的选择。

我有几个问题:

  1. 如何声明 ABC 中的内部类,以便它能够很好地配合CRTP。我需要为 ABC 复制它吗?它可以是 A 中的空类,然后用专门的实现将它们屏蔽在 BC 中吗?

  2. 如何以更少的粘合和更少的重复来公开迭代器?

我不想与 boost 这样的外部库创建依赖关系,并且只想坚持使用 std。所以我必须自己实现我需要的任何额外内容。感谢您的所有帮助。

I am cutting my teeth at some template programming and I am very new to this. What I want to implement are a few CRTP classes that contain an STL container. Let class A{}; serve as an example for the (compile time) base class from which class B{}; and class C{}; are "derived" at compile time following the CRTP style.

Now both B and C will contain containers. For the purpose of the example let it be a std::vector and a std::set respectively. Now, I want to expose the iterators of these via a begin() and an end() function that exposes a forward iterator. However, I do not want to expose what is the exact container that is inside B and C and I want to define these functions for A, so that at call time the correct one for B and C get used.

Is this possible ? Right now my plan is to have a Iterator inner class for B as well as C that will contain the actual iterator of (a vector or a set as the case may be) and delegate the call to it. However this seems to be a lot of replicated glue code and I suspect there is a better option.

I have a couple of questions:

  1. How do I declare the inner clases in A, B and C so that it plays well with CRTP. Do I need to replicate it for A, B and C ? Can it be an empty class in A and I mask them in B and C with specialized implementations ?

  2. How can I expose the iterator with less glue and less duplication ?

I do not want to create dependencies with external libraries like boost and want to stick to std only. So I have to implement whatever extra I need myself. Thanks for all the help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

倾听心声的旋律 2024-12-09 01:36:52

也通过 CRTP 公开迭代器:

template <typename T, typename Iter, typename ConstIter>
struct Base
{
    Iter begin() { return static_cast<T*>(this)->begin(); }
    Iter end() { return static_cast<T*>(this)->end(); }
    ConstIter begin() const { return static_cast<const T*>(this)->begin(); }
    ConstIter end() const { return static_cast<const T*>(this)->end(); }
};


struct B : Base<B, std::vector<int>::iterator, std::vector<int>::const_iterator>
{
    std::vector<int>::iterator begin() { return container.begin(); }
    ...

private:
    std::vector<int> container;
};

如果您有更多类型要公开,则将特征类作为模板参数传递给 Base

template <typename T, typename Traits>
struct Base
{
    typename Traits::iterator begin() { ... }
    ...
};

// For this purpose, vector<int> makes a perfect traits class !
struct B : Base<B, std::vector<int> >
{
    std::vector<int>::iterator begin() { ... }
    ...
};

// Here is an example function taking Base as argument
template <typename T, typename Traits>
void foo(const Base<T, Traits>& x) 
{
    typename Traits::iterator i = x.begin();
    ...
}

Expose the iterator too via CRTP:

template <typename T, typename Iter, typename ConstIter>
struct Base
{
    Iter begin() { return static_cast<T*>(this)->begin(); }
    Iter end() { return static_cast<T*>(this)->end(); }
    ConstIter begin() const { return static_cast<const T*>(this)->begin(); }
    ConstIter end() const { return static_cast<const T*>(this)->end(); }
};


struct B : Base<B, std::vector<int>::iterator, std::vector<int>::const_iterator>
{
    std::vector<int>::iterator begin() { return container.begin(); }
    ...

private:
    std::vector<int> container;
};

If you have more types to expose, then pass a traits class as a template argument to Base:

template <typename T, typename Traits>
struct Base
{
    typename Traits::iterator begin() { ... }
    ...
};

// For this purpose, vector<int> makes a perfect traits class !
struct B : Base<B, std::vector<int> >
{
    std::vector<int>::iterator begin() { ... }
    ...
};

// Here is an example function taking Base as argument
template <typename T, typename Traits>
void foo(const Base<T, Traits>& x) 
{
    typename Traits::iterator i = x.begin();
    ...
}
思念绕指尖 2024-12-09 01:36:52

如果我理解正确的话,你正在寻找这样的东西。请注意,我制作了一些简单的构造函数只是为了说明它的工作原理。另外,你的class A是我的class TWrapperBaseB - TWrapperBC - TWrapperC。另一件事,对于这个特定的示例,您实际上并不需要有两个派生类,但我假设您的类 BC 有显着不同,以证明它在您的程序中是合理的。

编辑:忘记在循环中增加lIterSet。

#include <vector>
#include <set>
#include <iostream>

template< typename PType, typename PContainer >
class TWrapperBase
{
 public:
  typedef PType TType;
  typedef PContainer TContainer;
  typedef typename TContainer::iterator TIterator;
 protected:
  TContainer mContainer;
 public:
  TWrapperBase( const TContainer& pOriginal ) :
   mContainer( pOriginal )
  {
  }
  TIterator begin( void )
  {
   return mContainer.begin();
  }
  TIterator end( void )
  {
   return mContainer.end();
  }
};

template< typename PType, class PContainer = std::vector< PType > >
class TWrapperB : public TWrapperBase< PType, PContainer >
{
 public:
  TWrapperB( const TContainer& pOriginal ) :
   TWrapperBase( pOriginal )
  {
  }
};

template< typename PType, class PContainer = std::set< PType > >
class TWrapperC : public TWrapperBase< PType, PContainer >
{
 public:
  TWrapperC( const TContainer& pOriginal ) :
   TWrapperBase( pOriginal )
  {
  }
};

int main( void )
{
 int lInit[] =
 {
 1, 2, 3
 };

 std::vector< int > lVec( lInit, lInit + 3 );
 std::set< int > lSet( lInit, lInit + 3 );

 TWrapperB< int > lB( lVec );
 TWrapperC< int > lC( lSet );

 std::vector< int >::iterator lIterVec = lB.begin();
 std::set< int >::iterator lIterSet = lC.begin();

 while( lIterVec < lB.end() )
 {
  std::cout << "vector: " << *lIterVec << " / set: " << *lIterSet << std::endl;
  lIterVec++;    
  lIterSet++;
 }

 return 0;
}

If I understood you corretcly, you are looking for something like this. Note, I made some simple constructor just to illustrate that it works. Also, your class A is mine class TWrapperBase, B - TWrapperB, C - TWrapperC. Another thing, you don't really need to have two derived classes for this particular example, but I assume your classes B and C are significantly different to justify it in your program.

EDIT: Forgot to increment lIterSet in the loop.

#include <vector>
#include <set>
#include <iostream>

template< typename PType, typename PContainer >
class TWrapperBase
{
 public:
  typedef PType TType;
  typedef PContainer TContainer;
  typedef typename TContainer::iterator TIterator;
 protected:
  TContainer mContainer;
 public:
  TWrapperBase( const TContainer& pOriginal ) :
   mContainer( pOriginal )
  {
  }
  TIterator begin( void )
  {
   return mContainer.begin();
  }
  TIterator end( void )
  {
   return mContainer.end();
  }
};

template< typename PType, class PContainer = std::vector< PType > >
class TWrapperB : public TWrapperBase< PType, PContainer >
{
 public:
  TWrapperB( const TContainer& pOriginal ) :
   TWrapperBase( pOriginal )
  {
  }
};

template< typename PType, class PContainer = std::set< PType > >
class TWrapperC : public TWrapperBase< PType, PContainer >
{
 public:
  TWrapperC( const TContainer& pOriginal ) :
   TWrapperBase( pOriginal )
  {
  }
};

int main( void )
{
 int lInit[] =
 {
 1, 2, 3
 };

 std::vector< int > lVec( lInit, lInit + 3 );
 std::set< int > lSet( lInit, lInit + 3 );

 TWrapperB< int > lB( lVec );
 TWrapperC< int > lC( lSet );

 std::vector< int >::iterator lIterVec = lB.begin();
 std::set< int >::iterator lIterSet = lC.begin();

 while( lIterVec < lB.end() )
 {
  std::cout << "vector: " << *lIterVec << " / set: " << *lIterSet << std::endl;
  lIterVec++;    
  lIterSet++;
 }

 return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文