. |
Last Month's Obfuscated C++
Last month we asked you to explain the output of the following program:
#include
#include
using namespace std;
template
void g(int size, op o, const char* sep = "") {
for(int i = 0; i < size;="" ++i)="" cout="">< o(i)="">< sep;="" }="">
struct w {
char* operator()(int a) {
return g(size,binder1st(op(),a),"\t"),"\n";
}
};
template
void
x(){
g(size, w());
}
int main(){
x<>,6>();
return 0;
}
This constructor of the x template prints a multiplication table of six rows and six columns, for example:
0 0 0 0 0 0
0 1 2 3 4 5
0 2 4 6 8 10
0 3 6 9 12 15
0 4 8 12 16 20
0 5 10 15 20 25
To understand how this works, we'll first examine the template function g(size,op,sep). op is a function object. The function object is "called" with the first size integers (starting at 0) and the results printed (separated by the string specified by the optional sep argument). We use this function twice.
The first call is in the template function x. It calls g, passing size and an object of type w<op,size>. For our example, this will call g(6,w<multiplies<int>,6>,""), which will cause the following invocations to occur:
w<'multiplies<int>,6>::operator()(0); //rownum = 0
w<multiplies<int>,6>::operator()(1); //rownum = 1
...
w<multiplies<int>,6>::operator()(5); //rownum = 5
Each of these constructors prints a single row of the table. It does this by creating a new function object
binder1st<multiplies<int> >(multiplies(),rownum)
where rownum is the row being printed. This binder1st object is passed to g(), which invokes operator() on the object for each of the integers from 0 to size-1. This prints a single row of the table, using a tab as the separator argument.
binder1st is a standard template class defined in the <functional> header. It takes a binary function object (in this case, multiplies<int>) and a single operand, and creates a new unary function object that "binds" the operand as the first operand of the binary function. This resulting object is invoked by calling operator(), passing a single operand. operator() returns the result of applying the original binary function to the "bound" operand and the operand supplied at the call.
In our example, the row index is "bound" as the first operand. For each column, the invocation of the binder1st object invokes multiplies<int>(int,int), passing the row and column indices respectively; the result is printed by g.
For bonus credit: Explain how the newline at the end of each row is printed.
Parameterizing the operation as a function object allows us to generate tables for other operations. For instance, x<plus<int>,4>() gives us:
0 1 2 3
1 2 3 4
2 3 4 5
3 4 5 6
|
This Month's Obfuscated C++
This month's Obfuscated C++ tests your knowledge of some of the standard template libraries. What is the output of the following program?
#include <iostream>
#include <list>
#include <iterator>
#include <algorithm>
struct {
void operator()(int i){cout<<i<<"\n";}
} c;
int main(){
list<int> il(2,1);
list<int>::iterator i1(il.begin()),i2(i1);i2++;
back_insert_iterator<list<int> >i3(il);
for(;*i1<20000;i1++,++i2)
*i3=*i1+*i2;
for_each(il.begin(),il.end(),c);
return 0;
}
Rob Murray is Director, Engineering at the Irvine office of Net Explorer, an object-oriented software consulting company based in Houston, TX. He has taught C++ at technical conferences since 1987 and is the author of C++ Strategies and Tactics. He was the founding editor of C++ Report and can be contacted at [email protected].
|