Search code examples
c++templatesc++11friend-class

How to declare a specialization of a template's friend function


Having a template:

template <typename T, template <typename ELEM, typename ALLOC=std::allocator<ELEM> > class Cont=std::vector>
class VehiclesContainer {
  public:
    VehiclesContainer(std::initializer_list<T> l):container(l){};
    virtual ~VehiclesContainer(){};
    virtual void addVehicle(T elem);
    virtual T getFirst() const;
    template
    <typename U, template <typename ELEM2, typename ALLOC=std::allocator<ELEM2> > class Cont2>
    friend std::ostream& operator<<(std::ostream& out, const VehiclesContainer<U,Cont2>& obj);
  private:
    Cont<T> container;
};

I have the operator<< as a friend class:

template
<typename T,template <typename ELEM,typename ALOC=std::allocator<ELEM> > class Cont>
std::ostream& operator<<(std::ostream& out,const VehiclesContainer<T,Cont>& obj){
    typename Cont<T>::const_iterator it;
    for(it=obj.container.begin(); it!=obj.container.end(); ++it)
        out << *it << " ";
    return out;
}

What I want to do is to have a specialization for that function for the Integers in which instead of a space there will be a - between the elements at the output. I tried

template
<int,template <typename ELEM,typename ALOC=std::allocator<ELEM> > class Cont>
std::ostream& operator<<(std::ostream& out,const VehiclesContainer<int,Cont>& obj){
    typename Cont<int>::const_iterator it;
    for(it=obj.container.begin(); it!=obj.container.end(); ++it)
        out << *it << "-";
    return out;
}

But when I compile having in the main

VehiclesContainer<int,std::vector > aStack1({10,20,30});
std::cout << aStack1;

The general form of the operator<< is called instead of my specialization. I suppose that I didn't really specialize it. Any help how a specialization for a friend class can be declared?

Solution based on the answer of WhozCraig

Forward declaration:

template <typename T, template <typename ELEM, typename ALLOC=std::allocator<ELEM> > class Cont=std::vector>
class VehiclesContainer;

template <typename T, template <typename ELEM, typename ALLOC=std::allocator<ELEM> > class Cont>
std::ostream& operator<< (std::ostream& out, const VehiclesContainer<T,Cont>& obj);

template <template <typename ELEM, typename ALLOC=std::allocator<ELEM> > class Cont>
std::ostream& operator<< (std::ostream& out, const VehiclesContainer<int,Cont>& obj);

Declaration inside the class:

friend std::ostream& operator << <T,Cont>(std::ostream&,
                const VehiclesContainer<T,Cont>&);

friend std::ostream& operator << <Cont>(std::ostream&,
                const VehiclesContainer<int,Cont>&);

Definition of the friend functions:

template <typename T, template <typename ELEM, typename ALLOC=std::allocator<ELEM> > class Cont>
std::ostream& operator <<(std::ostream& os, const VehiclesContainer<T,Cont>& obj)
{
    if (obj.container.size() > 0)
    {
        os << obj.container.front();
        for (auto it = std::next(obj.container.begin()); it != obj.container.end(); ++it)
            os << ' ' << *it;
    }
    return os;
}

template <template <typename ELEM, typename ALLOC=std::allocator<ELEM> > class Cont>
std::ostream& operator << (std::ostream& os, const VehiclesContainer<int,Cont>& obj)
{
    if (obj.container.size() > 0)
    {
        os << obj.container.front();
        for (auto it = std::next(obj.container.begin()); it != obj.container.end(); ++it)
            os << '-' << *it;
    }
    return os;
}

Solution

  • This is one way to do what you want. I took liberties of using variadics in the template parameters to save myself some typing, but ultimately the premise should hopefully be obvious:

    #include <iostream>
    #include <vector>
    #include <cstdlib>
    
    // forward declaration of template class
    template <class T, template<class, class...> class Cont = std::vector, class... Args>
    class VehiclesContainer;
    
    // generic template for all T and all container types
    template<class T, template<class, class...> class Cont = std::vector, class... Args>
    std::ostream& operator <<(std::ostream&, const VehiclesContainer<T,Cont,Args...>&);
    
    // specific template for only int and all container types
    template<template<class, class...> class Cont = std::vector, class... Args>
    std::ostream& operator << (std::ostream& os, const VehiclesContainer<int,Cont,Args...>& obj);
    
    
    template <class T, template<class, class...> class Cont, class... Args>
    class VehiclesContainer
    {
    public:
        VehiclesContainer(std::initializer_list<T> l)
            : container(l)
        {};
    
        // friend overloaded to `int` type
        friend std::ostream& operator << <T,Cont,Args...>(std::ostream&,
                    const VehiclesContainer<T,Cont,Args...>&);
    
        friend std::ostream& operator << <Cont, Args...>(std::ostream&,
                    const VehiclesContainer<int,Cont,Args...>&);
    
    private:
        Cont<T,Args...> container;
    };
    
    template<class T, template<class, class...> class Cont, class... Args>
    std::ostream& operator <<(std::ostream& os, const VehiclesContainer<T,Cont,Args...>& obj)
    {
        if (obj.container.size() > 0)
        {
            os << obj.container.front();
            for (auto it = std::next(obj.container.begin()); it != obj.container.end(); ++it)
                os << ' ' << *it;
        }
        return os;
    }
    
    template<template<class, class...> class Cont, class... Args>
    std::ostream& operator << (std::ostream& os, const VehiclesContainer<int,Cont,Args...>& obj)
    {
        if (obj.container.size() > 0)
        {
            os << obj.container.front();
            for (auto it = std::next(obj.container.begin()); it != obj.container.end(); ++it)
                os << '-' << *it;
        }
        return os;
    }
    
    
    int main()
    {
        VehiclesContainer<std::string> vcString { "Camero", "Corvette" };
        VehiclesContainer<int> vcInt { 1,2,3 };
    
        std::cout << vcString << '\n';
        std::cout << vcInt << '\n';
    
        return 0;
    }
    

    Output

    Camero Corvette
    1-2-3