
Andrea Torsello <torsello@dsi.unive.it> writes:
David Abrahams wrote:
Andrea Torsello <torsello@dsi.unive.it> writes:
No, in some implementations you can just not provide the explicit constructor at all (at least gcc works well without it). If you do that you do not have the direct initialization problem anymore. I used the explicit keyword to use my approach with compliers like Comeau that do not like if you do not provide a X(X const &) constructor. I actually omitted to say this on the first iteration of the sample code because I though I needed to focus on the general approach. In the library I use with gcc I simply do not provide the explicit copy ctor at all.
I really understood that's what you were saying. My point was that if you just leave out the fancy "move ctor" overloads and provide the X(X const&) ctor with Comeau, the RVOs kick in and you don't get any suboptimal copies... except when using direct initialization. Since your technique punts on moving in that case anyway, it seems like there's really zero advantage to using the explicit copy ctor at all.
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).
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. If you try the test with GCC and -DBOOST_IMPLICIT_MOVE_CTOR_FOR_COPYABLE_TYPES you'll see that technique 1 does 0 suboptimal copies and RVOs are suppressed, using move construction instead. If you try it with technique 2, but no explicit, and the same command-line you get 1 suboptimal copy (i.e. it works without explicit). If you then add explicit you get 4 suboptimal copies because direct initialization can no longer move. But who would want to suppress RVOs in favor of move construction, anyway?
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.
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?
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. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com