So, I was trying to write a function like this:
void append_to_stream(std::ostream &stream)
{ }
template <typename T, typename... Args>
void append_to_stream(std::ostream &stream, T first, Args&&... rest)
{
stream << first;
append_to_stream(stream, rest...);
}
and call it like:
append_to_stream(stream,
std::endl,
std::endl);
But this doesn't work. I get an error that says 'too many arguments' to the function. I've narrowed it down to the point I know that the std::endl
is guilty - probably because it's a function. I managed to 'solve' this by declaring a struct called endl
and define the <<operator
for it so that it simply calls std::endl
. This works but doesn't feel particularly good. Is it not possible to accept std::endl as a template argument? The function works for other types.
Edit: here's the error:
src/log/sinks/file_sink.cpp:62:21: error: too many arguments to function ‘void log::sinks::append_to_stream(std::string&, Args&& ...) [with Args = {}, std::string = std::basic_string<char>]’
Update
Trying to get the compiler to deduce the correct template arguments @MooingDuck suggested that a function of the following form could be used:
template<class e, class t, class a>
basic_ostream<e,t>&(*)(basic_ostream<e,t>&os) get_endl(basic_string<e,t,a>& s)
{
return std::endl<e,t>;
}
However, this doesn't compile.
Error:
src/log/sinks/file_sink.cpp:42:28: error: expected unqualified-id before ‘)’ token
src/log/sinks/file_sink.cpp:42:53: error: expected initializer before ‘get_endl’
Any ideas why? For the sake of compiling this, I've added using namespace std;
Which parameter is legal for non-type template? Explanation: The following are legal for non-type template parameters:integral or enumeration type, Pointer to object or pointer to function, Reference to object or reference to function, Pointer to member.
There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.
A non-type template argument provided within a template argument list is an expression whose value can be determined at compile time. Such arguments must be constant expressions, addresses of functions or objects with external linkage, or addresses of static class members.
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.
std::endl
is a template, not a function, and the compiler cannot resolve which endl
to use.
Try:
append_to_stream(std::cout,
std::endl<char, std::char_traits<char>>,
std::endl<char, std::char_traits<char>>);
Or, MooingDuck's solution (corrected):
template<class e, class t, class a> //string version
std::basic_ostream<e, t>& (*get_endl(const std::basic_string<e, t, a>&))
(std::basic_ostream<e, t>& )
{ return std::endl<e,t>; }
template<class e, class t> //stream version
std::basic_ostream<e, t>& (*get_endl(const std::basic_ostream<e, t>&))
(std::basic_ostream<e, t>& )
{ return std::endl<e,t>; }
int main () {
std::ostream& stream = std::cout;
append_to_stream(stream,
get_endl(stream),
get_endl(stream));
}
Here is get_endl solution, simplified by C++11 decltype
feature:
template<class e, class t, class a> //string version
auto get_endl(const std::basic_string<e, t, a>&)
-> decltype(&std::endl<e,t>)
{ return std::endl<e,t>; }
template<class e, class t> //stream version
auto get_endl(const std::basic_ostream<e,t>&)
-> decltype(&std::endl<e,t>)
{ return std::endl<e,t>; }
int main () {
std::ostream& stream = std::cout;
append_to_stream(stream,
get_endl(stream),
get_endl(stream));
}
Much easier than specifying template arguments or defining a whole new template(!) is resolving the overload by cast.
typedef std::ostream & (&omanip_t)( std::ostream & );
append_to_stream(stream,
static_cast< omanip_t >( std::endl ),
static_cast< omanip_t >( std::endl ) );
This will work for all manipulators, whereas some manipulators could be templated differently, for example if user-provided.
Also, you should pass T first
by either perfect forwarding or const reference. It doesn't make much sense to forward first, and then pass by value. Also, without a call to std::forward
, rvalue arguments will be passed by value… just following the idiom, it would be
template <typename T, typename... Args>
void append_to_stream(std::ostream &stream, T &&first, Args&&... rest)
{
stream << std::forward< T >( first );
append_to_stream(stream, std::forward< Args >( rest ) ... );
}
http://ideone.com/cw6Mc
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With