Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use std::vector as a template parameter or does it need to be std::vector<T>?

Tags:

c++

templates

I know this is a simple question but I just could not find the answer.

I am trying to do something like this but instead of with std::vector ultimately I want it to be std::shared_ptr or std::weak_ptr:

template <int dim, class ChunkClass, class PtrClass>
class BaseChunkWindow : public IChunkWindow<BaseChunkWindow<dim, ChunkClass, PtrClass>, IChunk<ChunkClass>> {
public:
...
private:
PtrClass< IChunk<ChunkClass> > ptr;  <-- compiler doesn't like this line, however IChunk<ChunkClass>* works
};
like image 627
Xavier Avatar asked Jun 04 '13 18:06

Xavier


2 Answers

It depends on what you are passing it to, if the template you're trying to instantiate takes as a parameter a class template accepting 2 (or in c++11 a variadic number of) types then you can pass std::vector to that. In most cases however, templates require types as parameters and you cannot pass the class template std::vector.

    template <class T>
    struct gimme_a_type{};

    template <template <class,class> class T>
    struct gimme_a_template{};

    gimme_a_type<std::vector> //<-- does not compile, expected a type, got a template
    gimme_a_type<std::vector<int> > //<-- compiles, expected a type, got a type
    gimme_a_template<std::vector> //<-- compiles, expected a template, got a template that has the correct signature
    gimme_a_template<std::vector<int> > //<-- does not compile, expected a template, got a type

In response to your edit, there are difficulties to using class templates as template parameters. Matching the number of parameters exactly is actually difficult to do when you have default arguments in the class template you're trying to pass (std::vector in our case). Notice that the example above required a class template that takes 2 types, not just one. This is because std::vector takes two parameters, the second is just defaulted to std::allocator<T> for us.

The following example demonstrates the issue:

    template <template <class, class> class Tem>
    struct A
    {
        Tem<int> v; //<-- fails to compile on gcc, Tem takes two parameters
        Tem<int, std::allocator<int> >; //<-- compiles, but requires a priori knowledge of Tem
    };

    template <template <class...> class Tem>
    struct A2
    {
      Tem<int> v; //<-- This C++11 example will work, but still isn't perfect.
    };

The C++11 example is better, but if someone passed a class that has as a signature template <class, bool = false> class A3 it fails again because A2 requires a class template that takes types and not a mix of types and non-types (false being the non-type template parameter in this example). So even though A3<int> would be a valid instantiation you couldn't pass that class to A2. The solution there is to always use types in template parameter lists and use the std::integral_constant wrapper template to pass integral constants around.

like image 72
SirGuy Avatar answered Oct 12 '22 00:10

SirGuy


There are a couple ways of doing it.

The limited way would be to use a template template parameter with just a limited number of parameters being passed, e.g. 3.

template<template<class,class,class> class Cont, class T, class V, class U>
void f(Cont<T,V,U>&& cont) {
    //...
}

However that's pretty limiting and can be hard to manage if you decide to change it in the future.

So you can do it like so with the new Variadic Templates in C++11:

template<template<class...> class Cont, typename F, typename... Rest>
void f(Cont<F, Rest...>&& cont) {
    //...
}

This would work on other containers or things and is probably much easier to manage.

like image 5
Rapptz Avatar answered Oct 12 '22 01:10

Rapptz