Nested C++ template parameters for functions

Viewed 37285

I want to have a templated function in C++, where one template parameter is itself a template of another template parameter. If that doesn't make any sense, take the following code that prints a std::vector that is templated on type T

template <typename T>
void print_vector(std::vector<T> &vec)
{
    for(auto v: vec)
        std::cout << v << " ";
    std::cout << std::endl;
}
...
std::vector<double> vec(5);
...
print_vector(vec);

I want to further generalize this function for STL containers other than vector. But I don't know how to "nest" the template parameters such that the container is templated on type T. I have tried the following with no success

template <typename T, template <typename TT> V>
void print_container(V<T> &con)
{
    for(auto c: con)
        std::cout << c << " ";
    std::cout << std::endl;
}
...
std::vector<double> vec(5);
...
print_container(vec);

I am sure this has been answered here before, but I can't find the search terms to find the answer.


Thanks @ForEveR. Your response was right on the money! All of the responses to my question observed that there is no need have the "storage" type T templated, with the following solution being adequate for the example I gave:

template <typename C>
void print_container(C &con)
{
    for(auto v: con)
        std::cout << v << " ";
    std::cout << std::endl;
}

Unfortunately, the actual use case that motivated the question was a little bit more complicated. The routine takes multiple containers, like this linear algebra example with a matrix and vector class:

template <typename MATRIX, typename VECTOR>
void mat_vec_multiply(const MATRIX &A, const VECTOR &x, VECTOR &y)
{
    // Implement y = A*x;
}

Assume that both the MATRIX and VECTOR classes have to be templated on the same underlying storage class (i.e., double, float, int, etc.). The idea is that by explicitly specifying T as a template parameter, we can enforce this:

template < typename T,
           template<typename> class MATRIX,
           template<typename> class VECTOR>
void mat_vec_multiply(const MATRIX<T> &A, const VECTOR<T> &x, VECTOR<T> &y)
{
    // Implement y = A*x;
}

Unfortunately, I am using the CUDA compiler nvcc, which doesn't have any support for C++11 constructs (I just used C++11 in my example because it is less verbose). So I can't use std::is_same and static_assert, though I suppose I could roll my own is_same (or use Boost) easily enough. What is the "best practice" in this case, where I want to enforce the common template parameter for the storage classes?

3 Answers

std::vector has two parameters, type and allocator. Try this

template <typename T, typename Alloc, template <typename, typename> class V>
void print_container(V<T, Alloc> &con)
{
}

print_container(vec);

This will work for vector, list, etc., but will not work with map, set.

However, since you use auto you can use C++11 and then you can to this:

template <typename T, template <typename, typename...> class V, typename... Args>
void print_container(V<T, Args...> &con)

or

template <template <typename, typename...> class V, typename... Args>
void print_container(V<Args...> &con)

and of course most simple way is to do something like

template<typename C>
void print_container(C& con)

probably with some checks for deduce, that C is really container.

template<typename C>
auto print_container(C& con) -> decltype(con.begin(), void())

You're better off not doing that at all; consider just templating on the container

template <typename C>
void print_container(const C& container)
{

    for(auto v: container)
        std::cout << v << " ";
    std::cout << std::endl;
}

If you need the stored type in the function, you can use: `typedef typename C::value_type T;

I am not sure that I understood what you want but you can try this:

template <typename V>
void print_vector(V &vec)
{
    for(auto v: vec)
        std::cout << v << " ";
    std::cout << std::endl;
}
...
std::vector<double> vec(5);
...
print_vector(vec);

The point here is that usually you don't need construct like template < template V< typename T> > because whole template template V< typename T> can be generalized to type V.

Related