Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a function template as a template argument?

#include <iostream>

template<typename... Args>
void print(Args const&... args)
{
    (std::cout << ... << args);
}

int main()
{
    std::cout << 1 << 2 << 3 << std::endl; // ok
    print(1, 2, 3);                        // ok
    print(1, 2, 3, std::endl);             // error! How to make it work?
}

See online demo

How to pass a function template as a template argument?

like image 786
xmllmx Avatar asked Aug 05 '21 11:08

xmllmx


People also ask

Can a template be a template parameter?

Templates can be template parameters. In this case, they are called template parameters. The container adaptors std::stack, std::queue, and std::priority_queue use per default a std::deque to hold their arguments, but you can use a different container.

Can a template parameter be a function?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.


Video Answer


3 Answers

You will have the same issue with other io manipulators that typically are functions that take the stream as parameter, when they are templates. Though you can wrap them in a non-template callable:

#include <iostream>

template<typename... Args>
void print(Args const&... args)
{
    (std::cout << ... << args);
}
    
int main()
{
    std::cout << 1 << 2 << 3 << std::endl; // ok
    print(1, 2, 3);                        // ok
    print(1, 2, 3, [](std::ostream& o) -> std::ostream&{ 
               o << std::endl; 
               return o;
    });             // no error!
}

Output:

123
123123

The syntax is rather heavy so you might want to use a helper type, though I'll leave it to you to write that (just joking, I don't think it is trivial, but I might give it a try later ;). After pondering about it for a while, I am almost certain that there are only the two alternatives: Instantiate the function (see other answer), or wrap the call inside a lambda, unless you want to write a wrapper for each single io manipulator of course.

like image 188
463035818_is_not_a_number Avatar answered Oct 20 '22 17:10

463035818_is_not_a_number


Here a way:

print(1, 2, 3, std::endl<char, std::char_traits<char>>);

Consider using '\n' instead.

like image 38
Ayxan Haqverdili Avatar answered Oct 20 '22 16:10

Ayxan Haqverdili


You cannot take address of most standard functions (see can-i-take-the-address-of-a-function-defined-in-standard-library).

Fortunately, io-manipulator is part of the exception (See Addressable_functions).

std::endl is a template function, so you would have to select the correct overload.

using print_manip_t = std::ostream& (*) (std::ostream&);

print(1, 2, 3, print_manip_t{std::endl});
print(1, 2, 3, static_cast<print_manip_t>(std::endl));
print(1, 2, 3, static_cast<std::ostream& (*) (std::ostream&)>(std::endl));

else you have to specify which one you want

print(1, 2, 3, std::endl<char, std::char_traits<char>>);

or wrap it

print(1, 2, 3, [](std::ostream& o) -> std::ostream&{ return o << std::endl; });

Demo

like image 5
Jarod42 Avatar answered Oct 20 '22 16:10

Jarod42