Search code examples
c++c++11templatesstltemplate-templates

template template class, call a function if it exists


I have this simple function with a template template parameter. It's meant to take an STL container, convert the smart ptr to a normal ptr (it's a C++03 project, but I'm also interested in the answer for C++11):

template <template <typename _T, typename = std::allocator<_T> > class Container>
static Container<T*> GetRawPtrContainer(const Container<SmartPtr<T> >& input_container)
{
    Container<T*> container;
    for(typename Container<SmartPtr<T> >::const_iterator it = input_container.begin();
        it != input_container.end();
        it++)
    {
        container.push_back(it->ptr);
    }
    return container;
}

This is a static member function of the class SmartPtr<T>.

You see here, all this does is push_back all the elements from input_container to another one and return.

You may have noticed that if the input is std::vector, then there's a performance issue with O(1) insertions, while this is fine for std::list and std::deque. So what I'd like to do is call this before the loop if it's possible (decided at compile-time):

container.reserve(input_container.size());

How can I do that?


Solution

  • Check if class has a reserve function:

    C++03:

    template<typename T> struct HasReserve {
        struct Fallback { void reserve(size_t); };
        struct Derived : T, Fallback { };
    
        template<typename C, C> struct Check;
    
        template<typename C> static char(&f(Check<void (Fallback::*)(size_t), &C::reserve>*))[1];
        template<typename C> static char(&f(...))[2];
    
        static bool const value = sizeof(f<Derived>(0)) == 2;
    };
    

    C++11:

    template <typename T, typename = int>
    struct HasReserve : std::false_type { };
    
    template <typename T>
    struct HasReserve <T, decltype(&T::reserve, 0)> : std::true_type { };
    

    Function that calls reserve if possible:

    template<typename T>
    typename std::enable_if<HasReserve<T>::value>::type
        Reserve(T& container, size_t s)
    {
        container.reserve(s);
    }
    
    template<typename T>
    typename std::enable_if<!HasReserve<T>::value>::type
    Reserve(T&, size_t)
    {
    }
    

    Just call the Reserve function before your loop and it should work like you want it to.

    template <template <typename _T, typename = std::allocator<_T> > class Container>
    static Container<T*> GetRawPtrContainer(const Container<SmartPtr<T> >& input_container)
    {
        Container<T*> container;
        Reserve(container, input_container.size()); // just add this to your function
        for(typename Container<SmartPtr<T> >::const_iterator it = input_container.begin();
            it != input_container.end();
            it++)
        {
            container.push_back(it->ptr);
        }
        return container;
    }
    

    std::enable_if for C++03

    template<bool B, class T = void>
    struct enable_if {};
    
    template<class T>
    struct enable_if<true, T> { typedef T type; };