How to pass a function template as a template argument?

Viewed 213
#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?

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.

Here a way:

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

Consider using '\n' instead.

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

Related