0

I want to write a C++ function like:

template <T<int> >
void printIntegers(T<int> ints) {
    for (int i: ints) printf("%d ", i);
}

Because I want T<int> to be either vector<int> or list<int> or any other STL container. How should I write the template parameter?

Neo
  • 1,031
  • 2
  • 11
  • 27

3 Answers3

4

You could take a template template parameter as the argument:

template <template <typename...> typename Container>
void printIntegers(Container<int> ints) {
  for (int i : ints) std::printf("%d ", i);
}

See https://en.cppreference.com/w/cpp/language/template_parameters#Template_template_parameter

As other answers have already suggested, though, taking by const reference may be preferable, and there are probably betters ways of doing your example anyway.

N. Shead
  • 3,828
  • 1
  • 16
  • 22
  • why the ... after typename? I don't see any guidance in the cppreference webpage. Anyway, I see that without them, the code won't compile. – Neo Jul 29 '20 at 11:47
  • 1
    Technically you don't need them for recent C++ standards, but some compilers (notably Clang) otherwise don't or didn't compile this. The quick reason is that `std::vector` and most other standard containers actually have (at least) two template parameters, `T` and (defaulted) `Alloc`, so we instead make it a variadic template in the template parameter to match as many types as necessary. – N. Shead Jul 30 '20 at 00:52
4

Don't overthink it.

template <typename Container>
void printIntegers(const Container& container)
{
    static_assert(std::is_same_v<typename Container::value_type, int>);
    
    for (const auto& el : container)
    {
       printf("%d ", el);
    }
}

Or even just:

template <typename Container>
void printThings(const Container& container)
{
    for (const auto& el : container)
    {
       std::cout << el << ' ';
    }
}
Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
  • Why a reference to int from the container... Compiler will optimize that indirection out, ok, but it did not help here for anything I believe. – Klaus Jul 27 '20 at 12:06
  • @Klaus *shrug* Don't copy things for no reason. Okay, in the first example, when we _know_ it's an `int`, perhaps just use `const auto`, but you're not passing it to a function so there's no overhead. I like to always write `const auto&` because why not. It describes precisely to the compiler what you want the program to mean. – Asteroids With Wings Jul 27 '20 at 12:12
  • I am with you to not copy anything. But for an int the overhead to take a "pointer" internally and dereference it later is uneeded overhead. Quite clear the compiler knows it and did not what you suggest :-) But it is still not needed if the type is known to be int. Only as a remark. Everything is fine for real world optimzing compilers :-) – Klaus Jul 27 '20 at 12:14
  • @Klaus There is no such overhead. No compiler will do that. It's not even what the semantics describe. `el` is just an alias, not a pointer. (You're thinking of function arguments where said references must be implemented by pointers.) – Asteroids With Wings Jul 27 '20 at 12:20
  • Surprisingly simple but effective solution! – Neo Jul 29 '20 at 11:48
3

You simply use a template template parameter ( template template parameter subsection there ) like this:

template < template < typename > typename T > void printIntegers( T<int>& container ) 
{
    for ( int el: container ) { std::cout << el << " " ; } 
    std::cout << std::endl;
}


int main()
{
    std::vector<int> i{1,2,3,4};
    std::list<int> l{7,8,9,10};

    printIntegers( i );
    printIntegers( l );
}

Some hints: In your code you did a copy instead of a reference by passing the container into your function. That will generate a lot of overhead by copying the content. The compiler may optimze it out, but you should write it with a reference to get a guarantee to not waste your memory with a copy.

Klaus
  • 24,205
  • 7
  • 58
  • 113
  • 1
    Clang refuses to compile this (incorrectly, see [this thread](https://stackoverflow.com/questions/47293162/how-is-p0522r0-breaking-code)), but `template template T` does work. Also, this way it doesn't seem to require C++17. – HolyBlackCat Jul 27 '20 at 12:03
  • Note that this requires C++17 standard. – eerorika Jul 27 '20 at 12:03
  • @eerorika: We have 2020, so I can assume that people are "already" using the last standard :-) Question was tagged c++ so I believe we can also use c++20 here. – Klaus Jul 27 '20 at 12:05
  • I wish we had not only C++20, but C++20 compilers as well – eerorika Jul 27 '20 at 12:07
  • @eerorika: gcc 10 has already some features and some of them are working :-) You are right, wrote some bug report last days, but hey, the future starts now! – Klaus Jul 27 '20 at 12:08
  • _"We have 2020, so I can assume that people are "already" using the last standard :-)"_ You really can't – Asteroids With Wings Jul 27 '20 at 12:12
  • @AsteroidsWithWings: If the author of a question wants to restrict the answer to some standard, he should tag also with the standard he wants. If we see c++ as the only tag, we can assume current standard! But my answer is fine with C++17 and that hopefully we all can expect :-) – Klaus Jul 27 '20 at 12:16
  • To be fair, although finalised, C++20 isn't even published yet, so it's not _really_ the latest standard :) – N. Shead Jul 27 '20 at 12:17
  • @Klaus The latest standard is C++17, and in practice many, many people can not use such cutting edge things yet (think Enterprise). That's just a fact of life. So, no, you cannot assume. Though the tagging _should_ indicate older (pre-C++17) standards, yes. – Asteroids With Wings Jul 27 '20 at 12:19
  • @AsteroidsWithWings: I am absolutely with you. But as long as a question is not tagged with a given standard, there is no need to restrict it to some older standard. Simply that. I know some people using still boarland compilers for education... all known! – Klaus Jul 27 '20 at 12:21