
David Abrahams wrote:
Andrea Torsello <torsello@dsi.unive.it> writes:
Sorry for the late reply: I have been away on Sunday. By the way, you guys are amazing:
I'm doubly flattered to be "pluralized" in that way! BTW, the message you're replying to is a pretty old message in the scheme of things; I've moved past much of what I said there.
Well, I couldn't be sure it was just you, so I erred on the side of caution...
I've been trying to convince the EDG guys that it's "obvious" that *if* you absolutely must choose between direct and copy initialization for the RVO, the intuitive choice is to go for direct initialization, because, well, it's "direct". That's the user perspective. The point is that from the point-of-view of a standards wonk and compiler implementor, there's no particular reason to think that RVO is easier in the direct case than in the other one.
I ment that the recieiving side is easyer that the returning side: it is harder to make sure that a function that returns a temporary does not make a copy than it is to make sure that a direct or assignment constructor is elided when passed a temporary. From a user perspective, the choice of copy-initialization (direct or assignmetn) should make no difference to RVO, no matter what language lawyers say about it.
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 ;)
I think you have a good point, actually. The two approaches each have their own strengths apparently, and I'm less convinced of the advantages of technique 1 than I used to be. OTOH, I'm not convinced there's any advantage at all to using an explicit copy ctor.
As your tests point out, technique #1 might force less moves with compilers that are not too aggressive when optimizing. I wonder if the two approaches should coexist, at least for a while and let the users choose between syntactic semplicity and (possibly) higher efficiency.
Maybe a nicer enabler should be provided, that allows us to write:
typename enable_lvalue<T, X, void>::type // no "const"
The enablers get more interesting if you want to write a generic function over a templated type Y<U>:
template <class T> typename enable_if<is_Y<T>,void>::type f(T& x);
fortunately I have macros to generate is_Y (see boost/python/detail/is_xxx.hpp). On the other hand, with technique 2 I think you can't write the generic function at all, because template arguments have to match exactly:
template <class T> void f(const_lvalue<Y<T> > x); // can never match a Y<T> argument
Right. That is why in my test code I had to explicitly qualify disctiminate<int> I wonder whether we can do something similar with technique #2... Something else I should look into.
I think you're possibly suggesting what I did in the example above? I must've misinterpreted you because technically there's no overload there.
Exactly.
Let's see, would you want a shared_ptr<Derived> to be _IMPLICITLY_ convertible to a shared_ptr<Base>? ;-) Seriously, most such converting ctors I've seen in the wild are of that nature. I don't think there's any way to generalize about whether explicit is usually needed.
Point taken. Different backgrounds, different use-cases in mind.