Question

I have 2 existing classes A and B. I want to implement a third struct C such that C implement operator() which would take any container of A or using a different implementation any container of B. Is it possible to do a such thing using template specialization ?

Was it helpful?

Solution

enable_if is very useful in lots of scenarios, but in cases like these I'm usually more inclined to use tag dispatching. To my eye, the code looks cleaner, behaves more predictably, and if you try to use it wrongly, the error messages make slightly more sense.

struct C
{
    template <class T>
    void operator()(const T& container) const
    {
        operator()(container, Tag<typename T::value_type>());
    }

private:
    template <class V> struct Tag {};

    template <class T>
    void operator()(const T& container, Tag<A>) const
    {
        std::cout << "A\n";
    }

    template <class T>
    void operator()(const T& container, Tag<B>) const
    {
        std::cout << "B\n";
    }
};

int main()
{
    std::vector<A> a;
    std::list<B> b;
    C c;
    c(a);
    c(b);
}

OTHER TIPS

You can do this using template template parameters.

Assuming your containers have two parameters (content type and allocator):

class A {};
class B {};

struct C {
  template <
    template <
      typename,
      typename
    >
    class V
  >
  void operator()(const V<A, std::allocator<A> >& /* ca */) const {
  }

  template <
    template <
      typename,
      typename
    >
    class V
  >
  void operator()(const V<B, std::allocator<B> >& /* cb */) const {
  }
};

Which would then allow you to do the following:

int main() {
  std::vector<A> va;
  std::vector<B> vb;

  std::list<A> la;
  std::list<B> lb;

  C c;

  c(va);
  c(la);

  c(vb);
  c(lb);
}

Overloads using std::enable_if and std::is_same seem to work. Not very pretty though. Note that these are c++11 features.

struct A {};
struct B {};
struct C {
    template<class Cont>
    void operator()(const Cont& c, typename std::enable_if<std::is_same<typename Cont::value_type, A>::value>::type * = NULL) const {
        std::cout << "a";
    }
    template<class Cont>
    void operator()(const Cont& c, typename std::enable_if<std::is_same<typename Cont::value_type, B>::value>::type * = NULL) const {
        std::cout << "b";
    }
};

int main() {
    std::vector<A> a;
    std::list<B> b;
    C c;
    c(a);
    c(b);
}

Yes it should be possible. Arkanosis has a good solution but here is if you want to use template specialization:

class A {};
class B {};

template <template <typename , typename > class Z, class Elem> struct C
{    
    void operator()(const Z<Elem, std::allocator<Elem>>& v) {   std::cout << "General implement" << std::endl; }
};

template <template <typename, typename > class Z> struct C<Z,B>
{    
    void operator()(const Z<B, std::allocator<B>>& v) {   std::cout << "Specialized implement" << std::endl; }
};

Then some client code:

    #include <iostream>
    #include <vector>
    using namespace std;
    int main(int argc, char** argv) 
    {
    std::vector<A> as;
    std::list<B> bs;
    C<vector,A> ca;
    ca(as);
    C<list,B> cb;
    cb(bs);
    }

If you want to handle different container types from the same C-style class you can derive from templates of the C struct e.g. class MultiContainerHandlerForA : public C<vector,A>, public C<list,A>.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top