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?

12
  • it should only accept containers of int ? Commented Jul 27, 2020 at 11:47
  • 2
    You would have to write a template template parameter. That said, it would be simpler to simply have a template type argument for what is passed directly. Commented Jul 27, 2020 at 11:48
  • why dont you define vec<T> instead T<int> as parameter Commented Jul 27, 2020 at 11:48
  • 1
    If you'd use std::cout << instead of printf, this problem would be gone (unless printing is just an example). Commented Jul 27, 2020 at 11:54
  • Why not call it prints? Commented Jul 27, 2020 at 12:00

3 Answers 3

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 << ' ';
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

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 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.
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 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.)
Surprisingly simple but effective solution!
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.

2 Comments

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.
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.
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.

10 Comments

Clang refuses to compile this (incorrectly, see this thread), but template <typename...> template T does work. Also, this way it doesn't seem to require C++17.
Note that this requires C++17 standard.
@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.
I wish we had not only C++20, but C++20 compilers as well
@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!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.