Boost logo

Boost :

From: Andrea Torsello (torsello_at_[hidden])
Date: 2004-04-17 11:31:56


"David Abrahams" <dave_at_[hidden]> writes:
>
> > "David Abrahams" <dave_at_[hidden]> writes:
>
> My point is that if you disable the library mechanisms that make
> direct initialization move instead of copy from rvalues:
>
> X a(source()); // a case you're explicitly not handling
>
> then the sink(source()) example compiles in strict mode. The default
> setting of my library is now configured that way for comeau.

OK

> >> Please do. Also please use the latest version of the code in the
> >> sandbox to test against.
> >
> > Last test was a couple of months ago when I was working activly on this,
> > will do on most recent version I can find
>
> IIRC I hadn't even posted my thing a couple of months ago(?)

Your post was Feb 6th, I was working on this first half of February.

> Can you please show a succinct example of "less interactions" so that
> I can do a comparison?

1) You need to treat the templated conversion-copy-constructor I was talking
about differently:
    add a second default parameter with enable_if_different or something
like that.
2) When overloading a function between temporaries and non-temporaries you
have to use the
    enable_if_equal trick on return values while the changes with my
approach are restricted to
    the passed types
3) Non temporaries overloads must be templated. If you want to provide
explicit overload on
    another compilation unit you must explicitly overload for both X and X
const.

While all these are doable, they expose a lot of details of the internals of
the library to the user.
With my approach The only requirement for #1 is that the
conversion-copy-constructor be explicit
(something that you better do anyway). If you really want to keep the
converting constructor implicit, then you have to use the
enable_if_different trick.

> >> Actually I'm not sure what I was trying to say anymore. I can state
> >> that my library already allows
> >>
> >> sink(source());
> >>
> >> to work transparently with move semantics on all compilers I've
> >> tested on.
> >
> > as stated before if sink is "void sink(X const &);" Comeau in strict
mode
> > complains that there is no X(X const &) constructor.
>
> As stated before, no it doesn't. Please test my code first and make
> claims about it later.
>
> >> Sorry, that wans just a mistake; it isn't convertible to anything. I
> >> meant:
> >>
> >> template <class T> // lvalues
> >> typename enable_if_same<T const, X const, void>::type f(T&);
> >>
> >> void f(move_from<X>); // temporaries
> >
> > Oh, OK that would work with operators as well, still with my approach it
> > would be:
> >
> > void f(X&);
> > void f(X::constant);
> > void f(X::temporary);
> >
> > which, to me, seems easier from the user perspective. Clearly, this is
just
> > a matter of taste.
>
> Not entirely. Your approach entails some code duplication between the
> first two overloadsq. I guess the first f can be:
>
> void f(X& x) { X::constant(x); }

Actually "void f(X& x) { f(X::constant(x)); }", but yes.

> But then what happens when f takes two or three arguments, both of
> which might be movable rvalues? I see an exponential explosion of
> overloads coming.

So does your approach. base 2 exponential explosion instead of base 3, but
exponential explosion all the same. Furthermore, if you want to provide the
explicit
instantiation on a separate compilation unit, you wold have a base 3
explosion since
you wold have 2 instantiation for each non-temporary type. Furthermore, the
higher
the number of arguments that need to be differentiated, the longer and
uglier the return
type would be.
Note that if you don't need to differentiate, you can just pass by value and
trust the copy
constructor to move temporaries.

> > Yes. I was not claiming that forcing a templated function was an
> > unsurmoutable problem, obviously there are ways aroud it. But think
> > from the perspective of the user: even if you hide the details of
> > the function definition with macro tricks, suddenly the user cannot
> > keep the function on a separate compilation unit unless the macro
> > manages to perform the explicit instantiation as well, which would
> > be hard since you should explicitly instantiate two specializations:
> > one for type X and one for type X const.
>
> What macro tricks? I only use those for the copy ctor and assignment
> operator because those may otherwise entail duplicating the function
> body.

I am not saying that you are using macro tricks. I am saying that the way
you
differentiate between temporaries and non-temporaries on function overload
is
a bit cumbersome in the non-temporary case and exposes a lot of details of
the
approach (templated code, enable_if_equal on the return type etc..).
You might try to hide all this with macro trickery to make it easier on the
user,
but then you wold have to figure out how to explicitly instantiate the
function for
both X and X const. The mechanism behind the move abstraction leaks out to
the
user in a lot of cases.

>
> Sorry, you wrote:
>
> "I chose to keep the temporary and constant subtypes from mojo
> because they allow me to discriminate between const values,
> non-const rvalues and non-const rvalues. The lack of this
> capability was the major problem I had with David Abrahams' move."
>
> I'm still trying to understand why it's a problem.

As a mater of fact I realized that with explicit instantiation of the
non-temporary
function overload your approach can still differentiate between const and
non-const
lvalues: non-const lvalues would pick the X overload, while the const
lvalues would
pick the X const overload. So this point is mute. Sorry.

Regards
Andrea Torsello


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