Why does transform() needs a struct and an operator()?

A

Alex Buell

I've just written the below as an exercise (don't worry about the lack
of checking), but I was wondering why I needed to write a struct with
an operator() as a parameter to supply to the STL transform() function?

#include <algorithm>
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
#include <string>
#include <cctype>

using namespace std;

struct lowercase
{
string operator()(const string& s)
{
string lower(s);

for (size_t i = 0; i < s.length(); ++i)
lower = tolower(lower);

return lower;
}
};

int main(int argc, char* argv[])
{
if (argc > 1)
{
ifstream in(argv[1]);
vector<string> vs;

copy(istream_iterator<string>(in),
istream_iterator<string>(), back_inserter(vs)); transform(vs.begin(),
vs.end(), vs.begin(), lowercase()); sort(vs.begin(), vs.end());

vector<string>::iterator it = unique(vs.begin(), vs.end
()); vs.resize(it - vs.begin());

copy(vs.begin(), vs.end(), ostream_iterator<string>
(cout, "\n")); }
}
 
K

Kai-Uwe Bux

Alex said:
I've just written the below as an exercise (don't worry about the lack
of checking), but I was wondering why I needed to write a struct with
an operator() as a parameter to supply to the STL transform() function?

You don't.

#include <algorithm>
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
#include <string>
#include <cctype>

using namespace std;

struct lowercase
{
string operator()(const string& s)
{
string lower(s);

for (size_t i = 0; i < s.length(); ++i)
lower = tolower(lower);

return lower;
}
};

int main(int argc, char* argv[])
{
if (argc > 1)
{
ifstream in(argv[1]);
vector<string> vs;

copy(istream_iterator<string>(in),
istream_iterator<string>(), back_inserter(vs)); transform(vs.begin(),
vs.end(), vs.begin(), lowercase()); sort(vs.begin(), vs.end());

vector<string>::iterator it = unique(vs.begin(), vs.end
()); vs.resize(it - vs.begin());

copy(vs.begin(), vs.end(), ostream_iterator<string>
(cout, "\n")); }
}


Consider:

#include <algorithm>
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
#include <string>
#include <cctype>

using namespace std;

string lowercase ( string const s ) {
string lower(s);
for (size_t i = 0; i < s.length(); ++i) {
lower = tolower(lower);
}
return lower;
}

int main(int argc, char* argv[]) {
if (argc > 1) {
ifstream in(argv[1]);
vector<string> vs;

copy(istream_iterator<string>(in),
istream_iterator<string>(),
back_inserter(vs));
transform(vs.begin(), vs.end(), vs.begin(), &lowercase);
sort(vs.begin(), vs.end());

vector<string>::iterator it =
unique(vs.begin(), vs.end());
vs.resize(it - vs.begin());

copy(vs.begin(), vs.end(),
ostream_iterator<string>(cout, "\n")); }
}


Now, which way is better in which ways is a different matter.


Best

Kai-Uwe Bux
 
V

Victor Bazarov

Alex said:
I've just written the below as an exercise (don't worry about the lack
of checking), but I was wondering why I needed to write a struct with
an operator() as a parameter to supply to the STL transform()
function?

You didn't. You could just write it as a stand-alone function.
Less typing, and you wouldn't need the () after it when calling
'transform'.

Tty it

string lowercase(const string& s)
{
... // your implementation
}

....
transform(vs.begin(), vs.end(), vs.begin(), lowercase);
#include <algorithm>
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
#include <string>
#include <cctype>

using namespace std;

struct lowercase
{
string operator()(const string& s)
{
string lower(s);

for (size_t i = 0; i < s.length(); ++i)
lower = tolower(lower);

return lower;
}
};

int main(int argc, char* argv[])
{
if (argc > 1)
{
ifstream in(argv[1]);
vector<string> vs;

copy(istream_iterator<string>(in),
istream_iterator<string>(), back_inserter(vs)); transform(vs.begin(),
vs.end(), vs.begin(), lowercase()); sort(vs.begin(), vs.end());

vector<string>::iterator it = unique(vs.begin(), vs.end
()); vs.resize(it - vs.begin());

copy(vs.begin(), vs.end(), ostream_iterator<string>
(cout, "\n")); }
}


V
 
A

Alex Buell

string lowercase ( string const s ) {
string lower(s);
for (size_t i = 0; i < s.length(); ++i) {
lower = tolower(lower);
}
return lower;
}


Thanks, I did just that when writing the exercise, but the compiler
wouldn't accept it - I know why now - it doesn't need the () when
calling a function. For operator () functions, it does need the ().
Thanks for pointing out the differences!

Thanks also to Bazarov!

Regards,
Alex
 
M

mathieu

Alex said:
I've just written the below as an exercise (don't worry about the lack
of checking), but I was wondering why I needed to write a struct with
an operator() as a parameter to supply to the STL transform() function?

You don't.


#include <algorithm>
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
#include <string>
#include <cctype>
using namespace std;
struct lowercase
{
string operator()(const string& s)
{
string lower(s);
for (size_t i = 0; i < s.length(); ++i)
lower = tolower(lower);

return lower;
}
};
int main(int argc, char* argv[])
{
if (argc > 1)
{
ifstream in(argv[1]);
vector<string> vs;
copy(istream_iterator<string>(in),
istream_iterator<string>(), back_inserter(vs)); transform(vs.begin(),
vs.end(), vs.begin(), lowercase()); sort(vs.begin(), vs.end());
vector<string>::iterator it = unique(vs.begin(), vs.end
()); vs.resize(it - vs.begin());
copy(vs.begin(), vs.end(), ostream_iterator<string>
(cout, "\n")); }
}

Consider:

#include <algorithm>
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
#include <string>
#include <cctype>

using namespace std;

string lowercase ( string const s ) {
string lower(s);
for (size_t i = 0; i < s.length(); ++i) {
lower = tolower(lower);
}
return lower;

}

int main(int argc, char* argv[]) {
if (argc > 1) {
ifstream in(argv[1]);
vector<string> vs;

copy(istream_iterator<string>(in),
istream_iterator<string>(),
back_inserter(vs));
transform(vs.begin(), vs.end(), vs.begin(), &lowercase);
sort(vs.begin(), vs.end());

vector<string>::iterator it =
unique(vs.begin(), vs.end());
vs.resize(it - vs.begin());

copy(vs.begin(), vs.end(),
ostream_iterator<string>(cout, "\n")); }

}

Now, which way is better in which ways is a different matter.


Is there actually any difference at all (if we except the passing by
copy in your implementation) ?

Thanks
-Mathieu
 
J

James Kanze

[...]
string lowercase ( string const s ) {
string lower(s);
for (size_t i = 0; i < s.length(); ++i) {
lower = tolower(lower);
}
return lower;
}
int main(int argc, char* argv[]) {
if (argc > 1) {
ifstream in(argv[1]);
vector<string> vs;
copy(istream_iterator<string>(in),
istream_iterator<string>(),
back_inserter(vs));
transform(vs.begin(), vs.end(), vs.begin(), &lowercase);
sort(vs.begin(), vs.end());
vector<string>::iterator it =
unique(vs.begin(), vs.end());
vs.resize(it - vs.begin());
copy(vs.begin(), vs.end(),
ostream_iterator<string>(cout, "\n")); }
}
Now, which way is better in which ways is a different matter.

Is there actually any difference at all (if we except the
passing by copy in your implementation) ?

One small difference: if you use a functional object, each
different function has a different type, so the compiler will
have to generate a distinct instantiation of the transform
function template for each different function you pass. All of
the pointers to function have the same type, so there is only
one instantiation.

Note that this means that with the functional object, the
compiler may be able to inline the function, and even if it
doesn't, it is a call to a fixed address, not through an
indirection. So you may get slightly more speed in return for
the code bloat.
 
M

mathieu

Alex Buell wrote:
I've just written the below as an exercise (don't worry about the lack
of checking), but I was wondering why I needed to write a struct with
an operator() as a parameter to supply to the STL transform() function?
You don't.
[...]


string lowercase ( string const s ) {
string lower(s);
for (size_t i = 0; i < s.length(); ++i) {
lower = tolower(lower);
}
return lower;
}
int main(int argc, char* argv[]) {
if (argc > 1) {
ifstream in(argv[1]);
vector<string> vs;
copy(istream_iterator<string>(in),
istream_iterator<string>(),
back_inserter(vs));
transform(vs.begin(), vs.end(), vs.begin(), &lowercase);
sort(vs.begin(), vs.end());
vector<string>::iterator it =
unique(vs.begin(), vs.end());
vs.resize(it - vs.begin());
copy(vs.begin(), vs.end(),
ostream_iterator<string>(cout, "\n")); }
}
Now, which way is better in which ways is a different matter.

Is there actually any difference at all (if we except the
passing by copy in your implementation) ?


One small difference: if you use a functional object, each
different function has a different type, so the compiler will
have to generate a distinct instantiation of the transform
function template for each different function you pass. All of
the pointers to function have the same type, so there is only
one instantiation.

Note that this means that with the functional object, the
compiler may be able to inline the function, and even if it
doesn't, it is a call to a fixed address, not through an
indirection. So you may get slightly more speed in return for
the code bloat.


Thanks !

-Mathieu
 
J

James Kanze

I've just written the below as an exercise (don't worry about
the lack of checking), but I was wondering why I needed to
write a struct with an operator() as a parameter to supply to
the STL transform() function?

Since no one has mentioned it yet...

[...]
struct lowercase
{
string operator()(const string& s)
{
string lower(s);
for (size_t i = 0; i < s.length(); ++i)
lower = tolower(lower);


This line, of course, results in undefined behavior on
implementations where plain char is signed. If you insist on
using the functions in <cctype> (instead of those in
<locale>---admittedly much more awkward), then you must cast:

lower[ i ] = tolower( static_cast< unsigned char >( lower[ i ] ) ;

And the usual reminder that this doesn't work in all (or even
most) languages: there's not always a one to one mapping of
lower to upper, or vice versa.
 
J

Joel Yliluoma

string lowercase ( string const s ) {
string lower(s);
for (size_t i = 0; i < s.length(); ++i) {
lower = tolower(lower);
}
return lower;
}


Thanks, I did just that when writing the exercise, but the compiler
wouldn't accept it - I know why now - it doesn't need the () when
calling a function. For operator () functions, it does need the ().


The reason the () are needed in the operator function
use is that the () creates an instance of the struct.
Without the (), you're only talking about a type.

struct gruu
{
bool operator() (...) const
{
}
};

void test()
{
somefunc(
gruu() // this creates a gruu object,
// and passes a reference to that
// instance to the function.
);

gruu tmp; // this also creates a gruu object,
// this time it's named "tmp" instead
// of being anonymous.

somefunc(tmp); // passes a reference to "tmp" to the function.
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top