Template Template C++ Function
Asked Answered
D

6

7

How do I write a template function that operates on a arbitrary container of a arbitrary type? For example how do I generalize this dummy function

template <typename Element>
void print_size(const std::vector<Element> & a)
{
    cout << a.size() << endl;
}

to

template <template<typename> class Container, typename Element>
void print_size(const Container<Element> & a)
{
    cout << a.size() << endl;
}

Here is a typical usage

std::vector<std::string> f;
print_size(f)

This give error

tests/t_distances.cpp:110:12: error: no matching function for call to ‘print(std::vector<std::basic_string<char> >&)’. I'm guessing I must tell the compiler something more specific about what types that are allowed.

What is this variant of template-use called and how do I fix it?

Displacement answered 26/9, 2011 at 12:55 Comment(1)
The answer to this question seems to be that the vector takes two template arguments, not one, so the template template parameter needs to be modified. The rest of the answers suggest using either a normal template parameter or, as of C++20, just the auto keyword.Evesham
L
14

Is there a specific reason for you to use a template template? Why not just like this?

template <typename Container>
void print_size(Container const& a)
{
    cout << a.size() << endl;
}

In general, template templates aren’t worth the trouble. In your particular case, there certainly is no use for them, and if you really need to access the member type, I suggest you bow to common practice and use a metafunction (typename Container::value_type in this case).

Lauralee answered 26/9, 2011 at 12:58 Comment(6)
What if you need to get the "contained" type ? Like if you want to create a function who gives you a std::map from two std::vector (one for the keys, the other for the values). But you could use something else than std::vector like a custom class with begin() and end() iterators.Grandchild
@Grandchild That’s what typendefs are for. Just use typename Container::value_type (note the prefixed typename, it’s important; there are other questions explaining why).Lauralee
is this value_type standard ? Well, I know it's in the stl for std::vector and so but is it the acknowledged way of doing it for custom classes ?Grandchild
@Grandchild Yes. Custom containers should do the same as native containers. If they don’t (I know some libraries where that’s the case), it’s their job to provide fitting adapters.Lauralee
This is exactly what concepts (in c++ terminology) is about, right ?Grandchild
This does not answer the question and is better left as a comment. I came here to get insight on template templates and this post is essentially useless. -1Evesham
M
7

I know that the question was asked long time ago , but i had same problem , and the solution is that you need to write

template <template <typename,typename> class Container, typename element, typename Allocator>
void print_size(Container<element, Allocator> & a)
{
    std::cout << a.size() << std::endl;
}

because the vector has two template parameters

Moron answered 7/4, 2015 at 13:48 Comment(0)
F
2

Why ever use something like

template <template<typename> class Container, typename Element>
void print_size(const Container<Element> & a)
{
    cout << a.size() << endl;
}

? Use it in simpler way:

template<typename Container>
void print_size(const Container & a)
{
    cout << a.size() << endl;
}

When calling print_size(f), you will call print_size with Container being vector<string>.

Festivity answered 26/9, 2011 at 12:59 Comment(0)
A
2

The problem is that the vector template takes two type arguments, and your template accepts only template arguments that accept a single argument. The simplest solution is lifting a bit of the type safety and just using Container as a type:

template <typename Container>
void print_size( Container const & c ) {
   std::cout << c.size() << std::endl;
}

Possibly adding static checks (whatever the type Container is, it must have a value_type nested type that is Element...) The alternative would be to make the template template argument match the templates that are to be passed (including allocator for sequences, allocator and order predicate for associative containers)...

Affidavit answered 26/9, 2011 at 13:1 Comment(0)
C
-1

For these types of generic algorithms, I always prefer iterators, i.e.

template <typename IteratorType>
void print_size(IteratorType begin, IteratorType end) {
   std::cout << std::distance(begin, end) << std::endl;
}

To call:

std::vector<std::string> f;
print_size(f.begin(), f.end());
Conakry answered 26/9, 2011 at 13:45 Comment(2)
Why would you prefer this, particularly seeing that distance can have worse algorithmic complexity than the size member function?Earpiece
@Earpiece - yes - that's true for non random-access iterators, in this case - there should be no difference. Anyway, this example is trivial, what I mean is that (all)most standard library algorithms work with iterators, so why not do your algorithms in the same way?Conakry
E
-1

From C++20, something like below is possible. Just declare print function with auto parameter.

void print_size(auto container)
{
    std::cout << container.size() << std::endl;
}

int main()
{
    std::vector vecOfInts = { 2,4,6,8 };
    std::map<int, int> mapOfInts = { {2,4},{6,8},{10,12} };

    print_size(vecOfInts);
    print_size(mapOfInts);
}
Effloresce answered 31/1, 2024 at 19:7 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.