Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2004-04-17 12:35:42

"Andrea Torsello" <torsello_at_[hidden]> writes:

> "David Abrahams" <dave_at_[hidden]> writes:
>> 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.

Oh, that? That was just an example of the technique; it wasn't
supposed to represent a library.

>>> It looks like my approach doesn't do anything your approach cannot
>>> do (not surprisingly since the only difference is in the technique
>>> I use to make X(X const &) less "priviledged" than the other
>>> constructors), but it appears to have less interactions with other
>>> language features. And hence less requirements on the library
>>> user.
>> Can you please show a succinct example of "less interactions" so
>> that I can do a comparison?

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.

> 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

> 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.

> 3) Non temporaries overloads must be templated.

I don't see that as a liability when the alternative is code
duplication or forwarding.

> 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

> 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?

> 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
    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.

>> > 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,

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

> 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.

I'd use optional arguments for that purpose, but you're right.

> Note that if you don't need to differentiate,
> you can just pass by value and trust the copy constructor to move
> temporaries.

Of course.

>> > 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.


Dave Abrahams
Boost Consulting

Boost list run by bdawes at, gregod at, cpdaniel at, john at