
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.
In non-strict mode (and on Intel 8/win32) technique 2 seems to generate copies when constructing from const rvalues whereas technique 1 does not.
Yes, actually different compilers seem to act differently here. Since the compiler is free to copy const rvalues to non-const rvalues and optimize the copy away, I would consider returning a const temporary an odd thing to do. I am curious: does anyone have a use-case for it?
I doubt it; it doesn't seem like a very important case. It could be used to have some effects on overload resolution I suppose, but it's hard to imagine that being useful in real code.
Not having a real T const& parameter for const lvalues makes writing overloads tricky. I was able to make the macros transparent for operator=, but because of initializer lists there's no way to do the same thing for constructors. const_lvalue<T> now has a conversion to T const&, so you can use boost::implicit_cast<T const&>(rhs) to get a reference to the argument with uniform syntax.
I am not sure I understood what you are saying here. Can you rephrase it please?
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. Technique 1 essentially uses (T& rhs) and (T const& rhs) signatures, so that's not a problem.
Results ------- [...]
We should probably try and not provide the explicit copy ctor with compilers that do not complain about it. I feel that this way some of the suboptimal copies will go away.
The suboptimal copies in my measurements for "technique 2" appear without any use of "explicit" on the copy ctor. Please, though, take a close look at what the code is doing to see if I've misinterpreted your intention. In particular, run the move.cpp test, then turn on -DBOOST_IMPLICIT_MOVE_CTOR_FOR_COPYABLE_TYPES and edit the code to make the ctor explicit. I think you'll see that it puts us back where we started. P.S. My tests are really the best part of what I've done on this; they reveal so much about what the compilers are doing, and tend to find problem cases by testing move-only types. I really want to encourage you to try whatever move ideas you may have in the testing framework I've set up. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com