C ++ 11 recursive variation patterns
I am trying to understand how recursive variational patterns work.
#include <iostream>
template<typename T>
static inline void WriteLog(T&& msg) {
std::wcout << std::forward<T>(msg);
}
template<typename T, typename... Ts>
static void WriteLog(T&& msg, Ts&&... Vals) {
WriteLog(std::forward<T>(msg));
WriteLog(std::forward<Ts>(Vals)...);
std::wcout << "\n**End**";
}
int main() {
WriteLog("apple, ", "orange, ", "mango");
}
Output:
apple, orange, mango
**End**
**End**
I was expecting only one thing **End**
. Why is it printed twice?
source to share
call tree:
WriteLog("apple, ", "orange, ", "mango");
->WriteLog("apple, ");
-> std::wcout << "apple, ";
->WriteLog( "orange, ", "mango");
->WriteLog("orange, ");
-> std::wcout << "orange, ";
->WriteLog( "mango");
-> std::wcout << "mango";
->std::wcout << "\n**End**";
->std::wcout << "\n**End**";
source to share
When the recursive call is WriteLog(std::forward<Ts>(Vals)...);
complete, it must execute the next statement. This function is called twice (once for "apple"
and once for "orange"
) and thus two printouts are written "**End**"
.
The last recursive call to "mango"
jumps directly to the first overload, since there is only one argument left in the package.
source to share
I'll be honest with you, I've been writing C ++ 11 code for 4 years now and I still have trouble remembering how to match an empty argument packet ...
this little trick avoids recursive template expansion: (EDIT: overwritten to support null arguments and set comma separator automatically)
#include <iostream>
namespace detail {
struct writer
{
template<class T>
void operator()(const T& t) {
if (_first) {
_first = false;
}
else {
std::cout << ", ";
}
std::cout << t;
}
private:
bool _first = true;
};
// non-template overload to catch no-parameter case
void do_write(writer&&)
{
}
// general case. Note w is passed by r-value reference
// to allow the caller to construct it in-place
template<typename T, typename...Ts>
void do_write(writer&& w, const T& t, Ts&&...ts)
{
w(t);
do_write(std::forward<writer>(w), std::forward<Ts>(ts)...);
}
}
// method 1 - no recursion
template<typename... Ts>
void WriteLog1(Ts&&... Vals) {
// expand one call for each parameter
// use comma operator to ensure expression result is an int
detail::writer write;
using expander = int[];
expander { 0, (write(std::forward<Ts>(Vals)), 0)... };
// write the final linefeed
std::cout << std::endl;
}
// method 2 - recursion
template<typename...Ts>
void WriteLog2(Ts&&...ts)
{
detail::do_write(detail::writer(), std::forward<Ts>(ts)...);
std::cout << std::endl;
}
int main() {
WriteLog1("apple", "orange", "mango");
WriteLog1("apple", "orange");
WriteLog1("apple");
WriteLog1("apple", 1.0, "orange", 1L, "mango", 2.6f);
WriteLog1(); // test pathalogical case
WriteLog2("apple", "orange", "mango");
WriteLog2("apple", "orange");
WriteLog2("apple");
WriteLog2("apple", 1.0, "orange", 1L, "mango", 2.6f);
WriteLog2(); // test pathalogical case
return 0;
}
output:
apple, orange, mango
apple, orange
apple
apple, 1, orange, 1, mango, 2.6
apple, orange, mango
apple, orange
apple
apple, 1, orange, 1, mango, 2.6
>
source to share