Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to simulate a partial specialization of selected member functions based on a template parameter that is an STL container?

I am working with a class that uses STL containers as a template parameter. Not all containers provide the same methods though, so I am trying to figure out how I can specialise specific methods based on the container used.

Example:

template<typename container>
class A
{
private:
    container m_container;
public:
    void foo(); // does something container specific

    // more generic methods that work with any container
};

The following code doexsn't compile saying "Can't match method specialisation", but this is approximately what I want to achieve:

template<typename T>
template<>
void A<std::vector<T> >::foo()
{
    // vector specific implementation
}

template<typename T>
template<>
void A<std::map<T> >::foo()
{
    // map specific implementation
}

I have to support a number of compilers including MSVC2010, gcc C++99, an old Solaris compiler...

The only way around this fiasco that I found was to implement external methods that do whatever foo is supposed to do and overload them for the different container types. But I don't want to expose those functions globally, is there a way to do it through specialisations?

Special case where it's not possible to outsource them is constructor specialisations...

like image 665
Sergey L. Avatar asked Dec 12 '14 13:12

Sergey L.


2 Answers

Option #1

Use tag-dispatching:

template <typename T>
struct tag {};

template <typename container>
class A
{
private:
    container m_container;    

    template <typename T, typename Alloc>
    void foo_spec(tag<std::vector<T, Alloc> >)
    {
        // vector specific implementation
    }

    template <typename K, typename V, typename C, typename Alloc>
    void foo_spec(tag<std::map<K,V,C,Alloc> >)
    {
        // map specific implementation
    }

public:
    void foo()
    {
        foo_spec(tag<container>());
    }

    // more generic methods that work with any container
};

DEMO 1

Option #2

Partially-specialize a separate class with a static member function:

template <typename T>
struct Impl;

template <typename T, typename Alloc>
struct Impl<std::vector<T, Alloc> >
{
    static void foo_spec()
    {
        // vector specific implementation
    }
};

template <typename K, typename V, typename C, typename Alloc>
struct Impl<std::map<K,V,C,Alloc> >
{
    static void foo_spec()
    {
        // map specific implementation
    }
};

template <typename container>
class A
{
private:
    container m_container;

public:
    void foo()
    {
        Impl<container>::foo_spec();
    }

    // more generic methods that work with any container
};

DEMO 2

Option #3

Derive from a partially specialized class + CRTP idiom:

template <typename container>
class A;

template <typename CRTP>
struct Base;

template <typename T, typename Alloc>
struct Base<A<std::vector<T, Alloc> > >
{
    void foo()
    {
        // vector specific implementation
        A<std::vector<T, Alloc> >* that = static_cast<A<std::vector<T, Alloc> >*>(this);
    }
};

template <typename K, typename V, typename C, typename Alloc>
struct Base<A<std::map<K,V,C,Alloc> > >
{
    void foo()
    {
        // map specific implementation
        A<std::map<K,V,C,Alloc> >* that = static_cast<A<std::map<K,V,C,Alloc> >*>(this);
    }
};

template <typename container>
class A : public Base<A<container> >
{
    friend struct Base<A<container> >;

private:
    container m_container;

public:
    // more generic methods that work with any container
};

DEMO 3

Option #4

Use an inversed inheritance hierarchy as proposed by David Rodríguez - dribeas in the comments:

template <typename container>
class Base
{
private:    
    container m_container;

public:
    // more generic methods that work with any container
};

template <typename container>
class A;

template <typename T, typename Alloc>
class A<std::vector<T, Alloc> > : public Base<std::vector<T, Alloc> >
{
public:
    void foo()
    {
        // vector specific implementation
    }
};

template <typename K, typename V, typename C, typename Alloc>
class A<std::map<K,V,C,Alloc> > : public Base<std::map<K,V,C,Alloc> >
{
public:
    void foo()
    {
        // map specific implementation
    }
};

DEMO 4

like image 57
Piotr Skotnicki Avatar answered Sep 19 '22 17:09

Piotr Skotnicki


You can't specialize individual methods, but the whole class template:

template<typename container>
class A
{
...
...
template<typename T>
class A<vector<T>>
{

If you want most methods to be common and only some methods different, you can inherit all of the specializations from a common base class.

template<typename container>
class A : public ABase<container>
{
...
...
template<typename T>
class A<vector<T>> : public ABase<vector<T>>
{
like image 24
Jay Miller Avatar answered Sep 20 '22 17:09

Jay Miller