I am cutting my teeth at som开发者_Go百科e 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:
How do I declare the inner clases in
A
,B
andC
so that it plays well with CRTP. Do I need to replicate it forA
,B
andC
? Can it be an empty class inA
and I mask them inB
andC
with specialized implementations ?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.
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();
...
}
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;
}
精彩评论