Boost logo

Boost :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2004-02-20 08:38:02


Brian McNamara wrote:
>
> Here's my rationale; tell me what you think:
>
> If you have a reference parameter, it's because you want to have an
> effect on an argument. Examples are "in-out" or "out" parameters,
> where the parameter value is changed/assigned by the function so that
> the new value can be used by the caller.
>
> As a result, passing a copy of the argument and having the function
> modify the copy (as in some cases in boost::lambda) means that the
> modification gets "lost". In the example
>
> app(_,x)(g); // copy x, pass copy to g
>
> "g" is oblivious to the fact that it's not being called as just
>
> g(x);
>
> Whatever side-effects "g" makes to its argument never make it back to
> the caller, since "app" is passing it a copy of "x".

With you so far, but...

> The "lost" information probably indicates an error in the program.

... I don't accept this assertion.

The experience with C++ before non-const references could not bind to
temporaries indicated that this is typically just what the programmer
intended.

Contrary to your hunch, ...

> (My hunch is that this is very rare; you don't
> typically "try to ignore" reference parameter modifications.)

... programmers do sometimes want to ignore reference parameter
modifications.

The reason for the "no non-const references to temporaries" rule is that
implicit conversions often lead to errors of the form:

    void f(int & x);

    double y;

    f(y);

where the programmer does not intend to throw away the result, since y is an
lvalue. However, the double to int implicit conversion leads to a temporary
being created, modified, and thrown away.

If you try the equivalent example with boost::bind:

#include <boost/bind.hpp>

void f(int & x);

int main()
{
    double y = 0;
    boost::bind(f, y)();
}

you'll receive a compile-time error.

Here are some examples to illustrate why Boost.Bind is (supposed to be, as
wel'll see later) cv-transparent:

#include <boost/bind.hpp>
#include <iostream>

struct F
{
    typedef void result_type;

    void operator()(int x)
    {
        std::cout << "F::operator()(" << x << ")\n";
    }
};

int main()
{
    F f;
    f(1);
    boost::bind(f, 1)();
}

Note that F::operator() is non-const.

Expanded version:

#include <boost/bind.hpp>
#include <iostream>

struct F
{
    typedef void result_type;

    void operator()(char const * x)
    {
        std::cout << "F::operator()(" << x << ")\n";
    }

    void operator()(char const * x) const
    {
        std::cout << "F::operator()(" << x << ") const\n";
    }
};

template<class F> void test(F f)
{
    f("test");
}

template<class F> void test2(F const & f)
{
    f("test2");
}

int main()
{
    test( F() );
    test2( F() );
    test( boost::bind( F(), _1 ) );
    test2( boost::bind( F(), _1 ) );
}

This actually exposes a bug in boost::bind. Oops. ;-) Will fix.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk