Search code examples
c++templatesvs-unit-testing-framework

Specialise template for vector of arbitrary type


I have a template function for converting an object to a string (this is from a library, I can't change this):

template <typename Q> std::wstring ToString(const Q& q) { static_assert(false, "Must specialise"); }

Now, I want to call it with a parameter of std::vector<Gene>, where Gene is a simple class whose details are not important. Of course, to do this, I need to specialise the template, so I do:

template<> std::wstring ToString(const std::vector<Gene>& q){...}

Suppose I have another class, Cell, and I want to specialise the ToString function for a std::vector<Cell>. I would have to make another explicit specialisation, with the same body as the std::vector<Gene> version.

The logic for converting a std::vector doesn't depend on the actual content type (an int, a Gene, a Cell, another std::vector, etc), so it would make sense to create a template specialisation that can work with any std::vector. But I can't figure out an easy way to do this. For now, I currently have a VectorToString function, and forward the call from ToString(std::vector<Gene>>) and ToString(std::vector<Cell>), but this still requires me to implement a specialisation for each element type.

So, to the actual question: is it possible to create a single specialisation for an arbitrary std::vector, and how would I do this? And, as a bonus question, could this be generalised to any arbitrary collection which supports std::begin and std::end?

I have looked at this this question and tried declaring a specialisation like this:

template<typename E> std::wstring ToString<std::vector<E>>(const std::vector<E>& t){...}

but this fails with C2768: illegal use of explicit template arguments ("The compiler was unable to determine if a function definition was supposed to be an explicit specialization of a function template or if the function definition was supposed to be for a new function."), which makes sense since we've got the same function name and number of template parameters, and a similar signature.

For reference, I am using Visual C++ 2015 RC, and the original template function originates from the native test library.


Solution

  • You almost did it correctly but in case of functions you shouldn't specialize them, you should simply overload them like so:

    template <typename Q> std::wstring ToString(const Q& q) {
        return L"Original";
    }
    template<typename E> std::wstring ToString(const std::vector<E>& t) {
        return L"overload";
    }
    

    See http://ideone.com/G3r0Vt for an running example.

    This works because when selecting the overload to call, const std::vector<E> is considered to be "better" than const Q& thus the overload is used.

    When using those methods in your code you should consider ADL.