
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.