Topic: Passing function objects by reference


Author: Atgeirr F Rasmussen <atgeirr@sintef.no>
Date: 2000/10/11
Raw View
I think the reason the smart trick (explicitly stating the template
argument to be a reference) does not work, is that the auxillary
template functions called by the top-level template function are NOT
explicitly qualified.

Your example:

Herve Bronnimann wrote:
> template <class Compare>
> void algorithm2(Compare comp) { clog << "algorithm2\n"; algorithm(comp);
> }

If you write

template <class Compare>
void algorithm2(Compare comp) { clog << "algorithm2\n";
algorithm<Compare>(comp);
}

instead, it will work as you expect it to (no copy constructor calls).
So this problem can be solved quite easily if you control the source
code, but if your standard library implementation has the (bad,
probably?) habit of not explicitly qualifying templates (mine doesn't,
at least not for std::sort), you may be hosed.

Atgeirr F Rasmussen
atgeirr@sintef.no
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: James Kanze <James.Kanze@dresdner-bank.de>
Date: 12 Oct 00 05:54:13 GMT
Raw View
Herve Bronnimann wrote:

> Dear all: I have an expensive function object, which holds some
> internal value (a vector, in fact). I don't want to use the copy
> constructor!  The problem is that the STL algorithms (in many
> implementations, and in fact this IS required by the standard, for
> understandable reasons) pass this object by value and not by
> reference.

    [proposed solution which doesn't work deleted...]

My solution in almost all such cases is to use a handle class.  The
functional object which you pass contains just a pointer to the one
you really want, and forwards the function to it.  This has several
advantages:
  - you don't copy large objects (your problem)
  - you can use virtual functions (a frequent problem in my code), and
  - you can maintain state, regardless of how many copies and when the
    algorithm makes.
The last point is very significant -- none of the standard algorithms
make the slightest guarantee in this respect.

--
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]




Author: Herve Bronnimann <hbr@poly.edu>
Date: 2000/10/10
Raw View
Dear all: I have an expensive function object, which holds some internal
value (a vector, in fact). I don't want to use the copy constructor!
The problem is that the STL algorithms (in many implementations, and in
fact this IS required by the standard, for understandable reasons) pass
this object by value and not by reference.

Now, a nice trick given by Nicolai Josuttis in his *great* book, the C++
template library, to force the algorithm to pass the object by reference
is to explicitly qualify the algorithm's template parameters
("The C++ template library", Josuttis, Section 8.1.2, page 298)
Here is a simple sketch of the example:

   template <class T> void bar(T t);
   ...
   vector<int> x;
   bar< vector<int>& >(x);

My problem is that it DOES NOT WORK with algorithms that have internal
auxiliary functions. Modify the example above by:

   template <class T> void bar(T t);
   template <class T> void foo(T t) { bar(t); }
   ...
   vector<int> x;
   foo< vector<int>& >(x);

This calls the vector<> copy constructor. In fact, even though the
function called is explicitly requested to be foo< vector<int>& >,
it is foo< vector<int> > which ends up being called.

I witness this behavior with g++-2.95.2 (on Linux PentiumIII, but I
expect it's independent of the architecture). Originally, I was using
foo == std::sort, and saw the example in Josuttis' book as bar ==
std::generate. It took me a while to pinpoint the difference between the
two algorithms that made one work properly but not the other.

MY QUESTION IS: is this a consequence of the standard, an
implementation feature proper to g++, and do you believe this should be
the correct implementation?

MY CONCERN IS: why should the presence of internal aux functions change
the behavior of a function call? And even if that were the normal
behavior, should we have to know how the library algorithms are
implemented? I contend that certainly we shouldn't.

Regards,
--
Herv


PS: Here is a more complete example of a function object which will
completely illustrate my point. Simply copy paste it, and run it!
And tell me if you find a different output, on what system, etc.

-------------------------------------------------------------------
// simple.C
#include <iostream>
using namespace std;

struct binary_pred {
  int i;
  binary_pred() : i(100) {}
  binary_pred( binary_pred const &c) : i(c.i) {
    clog << "copy constructor [with i=" << i++ << "]\n";
  }
  bool operator() (int j, int k) {
    clog << "bool operator() [with i=" << i++ << "]\n";
    return j==k;
  }
};

template <class Compare>
void algorithm(Compare comp) { clog << "algorithm\n"; comp(1,2); }

template <class Compare>
void algorithm2(Compare comp) { clog << "algorithm2\n"; algorithm(comp);
}

int main()
{
  binary_pred comp;
  clog << "Call with algorithm<binary_pred>\n";
  algorithm2<binary_pred> ( comp );
  clog << "Call with algorithm<binary_pred&>\n";
  algorithm2<binary_pred &> ( comp );
  clog << "Value of comp: " << comp.i << "\n";
}
--------------------------------------------------------------------

This outputs:

    Call with algorithm<const binary_pred>
    copy constructor [with i=100]
    algorithm2
    copy constructor [with i=101]
    algorithm
    bool operator() [with i=102]
    Call with algorithm<const binary_pred&>
    algorithm2
    copy constructor [with i=100]
    algorithm
    bool operator() [with i=101]
    Value of comp: 100

Replacing algorithm2() calls in main by algorithm() calls outputs:

    Call with algorithm<const binary_pred>
    copy constructor [with i=100]
    algorithm
    bool operator() [with i=101]
    Call with algorithm<const binary_pred&>
    algorithm
    bool operator() [with i=100]
    Value of comp: 101

To illustrate that there might be a problem with the compiler, if you
call algorithm2<const binary_pred &>(comp), it will not generate any
warnings, even though comp(1,2) is not a const member function. This is
because instead of algorithm2<const binary_pred &>, it calls
algorithm2<binary_pred>. You can force the compiler to show it by
replacing comp(1,2) by comp() and looking at the error message.


Sent via Deja.com http://www.deja.com/
Before you buy.

      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Yaroslav Mironov" <tada@mail.wplus.net>
Date: 2000/10/11
Raw View
Hi

Herve Bronnimann wrote:
>Dear all: I have an expensive function object, which holds some internal
>value (a vector, in fact). I don't want to use the copy constructor!
>The problem is that the STL algorithms (in many implementations, and in
>fact this IS required by the standard, for understandable reasons) pass
>this object by value and not by reference.
>
>Now, a nice trick given by Nicolai Josuttis in his *great* book, the C++
>template library, to force the algorithm to pass the object by reference
>is to explicitly qualify the algorithm's template parameters
>("The C++ template library", Josuttis, Section 8.1.2, page 298)
>Here is a simple sketch of the example:
>
>   template <class T> void bar(T t);
>   ...
>   vector<int> x;
>   bar< vector<int>& >(x);
>
>My problem is that it DOES NOT WORK with algorithms that have internal
>auxiliary functions. Modify the example above by:
>
>   template <class T> void bar(T t);
>   template <class T> void foo(T t) { bar(t); }
>   ...
>   vector<int> x;
>   foo< vector<int>& >(x);
>
>This calls the vector<> copy constructor. In fact, even though the
>function called is explicitly requested to be foo< vector<int>& >,
>it is foo< vector<int> > which ends up being called.


As far as I reckon, it is still foo<vector<int>& > that is being called. The
copy construction occures only when passing t into bar. Because when
deducing the template parameter, compiler strips references.

If you change the code to:

template <class T> void bar(T t);
template <class T> void foo(T t) { bar<T>(t); }

...

vector<int> x;
foo< vector<int>& >(x);

Explicitly stating the needed version of bar(), then the vector will be
passed by reference both times and no copy construction will happen. At
least that's how it works with Borland C++ 5.5.

[skip]

>--------------------------------------------------------------------
>
>This outputs:
>
>    Call with algorithm<const binary_pred>
>    copy constructor [with i=100]
>    algorithm2
>    copy constructor [with i=101]
>    algorithm
>    bool operator() [with i=102]
>    Call with algorithm<const binary_pred&>
>    algorithm2
>    copy constructor [with i=100]
>    algorithm
>    bool operator() [with i=101]
>    Value of comp: 100


See, there is only one copy construction in the case of a reference case
versus two otherwise.

>
>Replacing algorithm2() calls in main by algorithm() calls outputs:
>
>    Call with algorithm<const binary_pred>
>    copy constructor [with i=100]
>    algorithm
>    bool operator() [with i=101]
>    Call with algorithm<const binary_pred&>
>    algorithm
>    bool operator() [with i=100]
>    Value of comp: 101
>
>To illustrate that there might be a problem with the compiler, if you
>call algorithm2<const binary_pred &>(comp), it will not generate any
>warnings, even though comp(1,2) is not a const member function. This is
>because instead of algorithm2<const binary_pred &>, it calls
>algorithm2<binary_pred>. You can force the compiler to show it by
>replacing comp(1,2) by comp() and looking at the error message.


Is it?

Yaroslav
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Anders Pytte <anders@milkweed.com>
Date: 2000/10/11
Raw View
in article 8rii4f$hvq$1@nnrp1.deja.com, Herve Bronnimann at hbr@poly.edu
wrote on 10/10/00 11:22 AM:

> Dear all: I have an expensive function object, which holds some internal
> value (a vector, in fact). I don't want to use the copy constructor!
> The problem is that the STL algorithms (in many implementations, and in
> fact this IS required by the standard, for understandable reasons) pass
> this object by value and not by reference.

How about holding a pointer (better yet, auto_ptr) to a vector to reduce the
cost of copying?

Anders.

--
Anders Pytte                                   Milkweed Software
PO Box 32                                  voice: (802) 586-2545
Craftsbury, VT 05826                  email: anders@milkweed.com
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: James Kuyper <kuyper@wizard.net>
Date: 2000/10/11
Raw View
Herve Bronnimann wrote:
>
> Dear all: I have an expensive function object, which holds some internal
> value (a vector, in fact). I don't want to use the copy constructor!
> The problem is that the STL algorithms (in many implementations, and in
> fact this IS required by the standard, for understandable reasons) pass
> this object by value and not by reference.

One simple solution: instead of making your function object so heavy by
having it contain a vector object, change it to contain a pointer or
reference to the vector instead. You can make the pointer a
reference-counted smart pointer that automatically destroys the object
when the reference count reaches 0, or simply manage the vector's
creation and destruction outside the function object.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]