snippetcppMinor
writeln() and format() with variadic templates
Viewed 0 times
formatwithandtemplatesvariadicwriteln
Problem
I wanted to get better acquainted with variadic templates, so I decide to try to implement a function like D's
Usage example:
Next, I implemented a
It is uses
My concerns are:
-
I haven't had many chances to use variadic templates so far, so I might be missing some caveat here. I was expecting it to be more complicated... Did I miss some corner case?
-
Is my use of
-
Any other comments and suggestion are very welcome.
writeln(), just for fun.void writeln()
{
std::cout
void writeln(T firstArg, Args... extraArgs)
{
std::cout (extraArgs)...);
}Usage example:
writeln("hello ", "world ", 42, "\t", 3.141592);
writeln(); // prints just a newlineNext, I implemented a
format() function, which writes to a string instead of cout:// Need this because there is no to_string for string in the std namespace.
std::string to_string(const std::string & s)
{
return s;
}
std::string format()
{
return "";
}
template
std::string format(T firstArg, Args... extraArgs)
{
using namespace std;
std::string s = to_string(firstArg);
s += format(std::forward(extraArgs)...);
return s;
}
// sample:
std::string s = format("hello ", "world ", 42, "\t", 3.141592);It is uses
std::to_string() for the native types. If I want to print custom types, then I can define a local to_string() and the overload resolution should find it.My concerns are:
-
I haven't had many chances to use variadic templates so far, so I might be missing some caveat here. I was expecting it to be more complicated... Did I miss some corner case?
-
Is my use of
std::forward appropriate?-
Any other comments and suggestion are very welcome.
Solution
The biggest thing that sticks out at me is that you're passing all of your
Basically, everywhere you have
As a cut down example of the difference this makes, try this example:
If I run this, the output is:
In sink
If I change the signature of
The output is:
Copy
Move
In sink
If we change
We remove the first copy operation (from
Args... parameters by value, thus pretty much making std::forward useless here. To make use of std::forward, the reference type needs to be deduced from the calling context. By itself, std::forward really doesn't do anything except a static_cast to the deduced reference type.Basically, everywhere you have
Args should be passed by Args&&:template
void writeln(T firstArg, Args&&... extraArgs)
template
std::string format(T firstArg, Args&&... extraArgs)As a cut down example of the difference this makes, try this example:
#include
#include
struct s
{
s()
{ }
s(s&& )
{
std::cout
void do_forward(T&& v)
{
sink(std::forward(v));
}
template
void sink(U&& u)
{
std::cout << "In sink\n";
}
int main()
{
s something;
do_forward(something);
}If I run this, the output is:
In sink
If I change the signature of
do_foward and sink to:template
void do_forward(T v)
template
void sink(U u)The output is:
Copy
Move
In sink
If we change
main to just pass an actual rvalue reference instead of an lvalue:int main()
{
do_forward(s{});
}We remove the first copy operation (from
do_forward) but still have a move operation (in sink).Code Snippets
template<typename T, typename ...Args>
void writeln(T firstArg, Args&&... extraArgs)
template<typename T, typename ...Args>
std::string format(T firstArg, Args&&... extraArgs)#include <memory>
#include <iostream>
struct s
{
s()
{ }
s(s&& )
{
std::cout << "Move\n";
}
s(const s& )
{
std::cout << "Copy\n";
}
};
template <typename T>
void do_forward(T&& v)
{
sink(std::forward<T>(v));
}
template <typename U>
void sink(U&& u)
{
std::cout << "In sink\n";
}
int main()
{
s something;
do_forward(something);
}template <typename T>
void do_forward(T v)
template <typename U>
void sink(U u)int main()
{
do_forward(s{});
}Context
StackExchange Code Review Q#62485, answer score: 5
Revisions (0)
No revisions yet.