
David Abrahams wrote:
Oh, OK, if you assume that the compiler can always perform return value optimization, you are right. But I feel there is a difference in requiring the compiler to perform RVO on direct initialization from temporaries and requiring the compiler to perform it every time it is possible.
You appear to be making an argument about what's theoretically going to work in correct, portable C++, based on the behavior of a particular compiler implementation.
I am not requiring RVO unless you take what EDG accepts in strict mode as the defintion of "standards conforming". I do not. EDG is erroneous, IMO, in issuing that error, so we either turn off strict mode or work around it by allowing its native RVO to work. GCC has a much better example of what I consider a correct interpretation of the standard in requiring that it be possible to construct a new temporary rvalue from an existing one that will be bound to a reference (and not "copy construct", as I point out in N1610).
What I was saying is that if you provide a non-explicit X(X const &) constructor when passing a temporary by value you are performing a copy. So if you provide a non explicit copy ctor, you are requiring the compiler to perform RVO or you would have an extra copy. If you can assume the compiler can peform RVO in all cases why have the move construct at all?
Using the explicit constructor technique #2 works even with compilers that do not support RVO, as long as you use the assignment constructor instead of the direct copy constructor.
Well, I challenge you to find a compiler that doesn't support at least some RVO. But if you did find one, technique #1 is as good as technique #2 in that respect.
Yes.
But who would want to suppress RVOs in favor of move construction, anyway?
No-one, clearly. And Yes, all modern compilers support at least some RVO. I am just not sure whether this SOME is enough in all cases. I'd love to believe it is, but I am a bit scheptical.
You still need to convert to temporary in initializer lists, since you cannot use the assigment constructor there, but most likely you would have to do it anyway.
Is that just speculation? I can't understand why you'd make that claim, since the CWG thinks that a prohibition on copying rvalues is possible.
I believe we are talking about two different things here. I was thinking about cases where you have a movable class with movable members. class X : public movable<X> { ... }; class Y : public movable<y> { X x; public: Y(move_from<Y> y) : x(move_from<X>(y.x)) {} ^^^^^^^^^^^^^^^^^ clearly, "Y(move_from<Y> y) : x(y.x) {}" would not be able to move from x since in this context y.x is an lvalue.
The point is that if you want to use macros (or copy-and-paste) to generate the two overloads for (T& rhs) and (const_lvalue<T> rhs), you can have a problem trying to re-use the initializer list and body of the function because T& and const_lvalue<T> have different interfaces. With the appropriate conversion operator on const_lvalue, implicit_cast<T const&>(rhs) is the same thing in both cases and can be used to make initializer lists and bodies that can be reused.
Oh, right! There is probably no way around explicitly writing both constructors
I just got finished describing how it could be done using implicit_cast. Did you not understand that?
Sorry, didn't parse the last part correctly. You are right, if "const_lvalue" is convertible to T&, you can force the conversion in both initializer lists and still use a single macro. But it would still be the user's duty to explicitly force the conversion in initializer list, wouldn't it? I am not sure wether it is easier on the user to have him force a conversion in a situation that he normally would not than just tell him that he needs two separate constructors, one for const and one non-const lvalues.
but you can avoid duplication by putting the constructor's body in a private member function and forward from both constructors to it. At least that is what I have been doing.
If it were that easy you could do it with macros by inserting a surrounding brace pair and initializing a T const& there. Unfortunately, it doesn't handle the initializer list and I almost never have code in the ctor body.
Yes users would still have to provide the initializer lists. Clearly delegating the construction is worth only if the constructor has a non-trivial body