Boost logo

Boost :

From: Andrea Torsello (torsello_at_[hidden])
Date: 2004-04-19 07:42:17


Sorry for the late reply: I have been away on Sunday.
By the way, you guys are amazing: I go away for one day and you have already
inserted my alternative approach in the sandbox and thoroughly tested it.
WOW!

David Abrahams wrote:

> None of what you wrote below is "showing an example". I meant, please
> write down some code demonstrating that your technique "has less
> interactions with other language features". I'm not sure what "other"
> means in this case.

I meant that the user has to know less about the internals of the approach
when dealing with templated constructors and/or functions that distinguish
between temps and non-temps.
Anyway, I am downloading the sandbox right now. I will creating a few tests
and use cases to highlight the differences in usage patterns for the two
approaches. If anything, this will be useful for documentation purpose. Will
be back with as soon as my rent-paying job allows me to.

>> 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.
>
> OK, so I'll supply disable_if_same. I'm not sure where that leaves
> us, but I think my approach still handles more cases (including direct
> initialization on most compilers) with a similar amount of syntactic
> overhead.

Your approach handles direct initialization in more cases,
since my approach pretty much gives up moving direct initialization
and hopes the compiler can perform return value optimization.
In defense of my method I must say that this case is arguably
the simples RVO possible, which, unfortunately, doesn't mean that
it actually is simple nor that all compiler get it right,
as your tests clearly show.
As for the syntactic overhead, I believe that my approach has an
edge here, since it minimizes the amount of adjustments the user
has to go through in order to use it, but of course this is a
matter of personal opinion and I am certainly biased ;)

>> 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
>
> What's the significance of that? It doesn't change the function's
> returned type.

No, but it is one more thing the user has to do differently when
using the move library.

>
>> 3) Non temporaries overloads must be templated.
>
> I don't see that as a liability when the alternative is code
> duplication or forwarding.

Template code can be arbitrarily long and impacts compile-time
on each compilation unit using it, forwarding is one-line long,
almost certainly inlined by optimizing compilers, and has
negligible impact on compile-time.

>
>> If you want to provide explicit overload on another compilation unit
>> you must explicitly overload for both X and X const.
>
> No, that won't work. The rvalue will bind to the X const& parameter.
> It's as I said: you just omit the bodies and use explicit
> instantiation.

I was under the impression that template typee would always resolve
to the same type that the templated code would instantiate, i.e.
non-const lvalue would instantiate a X& version of the template
function and hence it would bind only to a X& explicit instantiation.
A quick check with gcc appears to show that the gcc people interpreted
the standard the same way, but I must admit I haven't looked into this
deeply enough to take a stance and I might very well be wrong here.
I will look into it and come back to you about it.

>> 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).
>
> Which one is that, and why had you better do it anyway?

 template <class U>
     X(X<U> const& other);

As for the fact that I believe that in most cases you should make it
explicit: would you want a vector<int> be _IMPLICITLY_ convertible
to a vector<double>?

>
>> If you really want to keep the converting constructor implicit, then
>> you have to use the enable_if_different trick.
>
> I think I do see a real issue here: In a templated converting
> constructor:
>
> template <class U>
> X(X<U> other);
>
> partial ordering fights against our interests by making that always a
> better match than a templated ctor:
>
> template <class U>
> X(T& other, typename enable_if_same<X const,U const>::type* = 0)
>
> and with my technique, there's nothing one can do to make partial
> ordering see the intended ctor as more specific, because there's no
> way to have the argument deduced as const:
>
> // won't bind to const lvalues
> template <class U>
> X(X<U>& other, typename enable_if_same<T,U>::type* = 0)
>
> So I'm wondering if there's a way to combine the advantages of your
> approach with those of mine.

You can always write the constructor as

        template<class U>
        X(X<U> const &, typename enable_if_different<T,U>::type=0)

which would make it impossible for the constructor to resolve to
        X(X<T> const &)

> You're right. That's why we need &&.

Couldn't possibly agree more or more emphatically!

Regards
Andrea Torsello

P.S. I am sorry if someone wrote you complaining about you telling me
to do some research. I must admit that, while I did look into the
sandbox, I didn't perform the same kind of extensive tests I did on
the previous version, and I obviously missed some of the latest fixes.
I do apologize for it, but, on my partial defense, I must say this is
not my paying job. As I said I developed this technique about two
months ago and then I was overwhelmed by my normal schedule.
If I tried to be too zelous I simply would have not proposed my
approach at all.


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