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

Discussion in 'C++' started by Alex Buell, Dec 11, 2007.

  1. Alex Buell

    Alex Buell Guest

    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")); }
    }


    --
    http://www.munted.org.uk

    Fearsome grindings.
     
    Alex Buell, Dec 11, 2007
    #1
    1. Advertising

  2. Alex Buell

    Kai-Uwe Bux Guest

    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.


    > #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
     
    Kai-Uwe Bux, Dec 11, 2007
    #2
    1. Advertising

  3. 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 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
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Dec 11, 2007
    #3
  4. Alex Buell

    Alex Buell Guest

    On Mon, 10 Dec 2007 20:28:37 -0500, I waved a wand and this message
    magically appears in front of Kai-Uwe Bux:

    > 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
    --
    http://www.munted.org.uk

    Fearsome grindings.
     
    Alex Buell, Dec 11, 2007
    #4
  5. Alex Buell

    mathieu Guest

    On Dec 11, 2:28 am, Kai-Uwe Bux <> wrote:
    > 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.
    >
    >
    >
    > > #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
     
    mathieu, Dec 11, 2007
    #5
  6. Alex Buell

    James Kanze Guest

    On Dec 11, 10:37 am, mathieu <> wrote:
    > On Dec 11, 2:28 am, Kai-Uwe Bux <> wrote:
    > > 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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Dec 11, 2007
    #6
  7. Alex Buell

    mathieu Guest

    On Dec 11, 11:26 am, James Kanze <> wrote:
    > On Dec 11, 10:37 am, mathieu <> wrote:
    >
    > > On Dec 11, 2:28 am, Kai-Uwe Bux <> wrote:
    > > > 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
     
    mathieu, Dec 11, 2007
    #7
  8. Alex Buell

    James Kanze Guest

    On Dec 11, 1:24 am, 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?


    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.

    > return lower;
    > }
    > };


    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Dec 12, 2007
    #8
  9. On Tue, 11 Dec 2007 09:26:50 +0000, Alex Buell wrote:
    > On Mon, 10 Dec 2007 20:28:37 -0500, I waved a wand and this message
    > magically appears in front of Kai-Uwe Bux:
    >
    >> 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.
    }

    --
    Joel Yliluoma - http://iki.fi/bisqwit/
     
    Joel Yliluoma, Dec 17, 2007
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. KathyB
    Replies:
    0
    Views:
    977
    KathyB
    Jun 24, 2003
  2. Chris Fogelklou
    Replies:
    36
    Views:
    1,405
    Chris Fogelklou
    Apr 20, 2004
  3. Mr. SweatyFinger

    why why why why why

    Mr. SweatyFinger, Nov 28, 2006, in forum: ASP .Net
    Replies:
    4
    Views:
    922
    Mark Rae
    Dec 21, 2006
  4. Mr. SweatyFinger
    Replies:
    2
    Views:
    2,076
    Smokey Grindel
    Dec 2, 2006
  5. cayblood
    Replies:
    4
    Views:
    2,561
    Ivan Vecerina
    Nov 3, 2005
Loading...

Share This Page