I'm CC'ing Howard, the master of the rvalue reference, and Russell, the author of the rvalue references patch... see below for my take on this.
On 5/7/07, Sylvain Pion <[EMAIL PROTECTED]> wrote:
I have done some experiments with the rvalue reference feature on the cxx0x-branch.
Great!
I am wondering: is this behavior (2 extra copies) required by the rvalue-reference specifications, or would gcc be allowed to do better?
In my understanding, GCC is allowed to elide the constructor, and should do so.
Even if the whole point of rvalue-reference copies is that they are supposed to be cheap (can we count on them to be fully optimized by the lower level optimizers, in principle?), it would probably be better to still be allowed to skip the 2 extra copies completely using the RVO, even for objects defining an rvalue-ref copy constructor, right?
Right. I've hacked up a fix (for ConceptGCC, but would be almost the same on cxx0x-branch) that elides the move constructor. The patch is below... it fixes your test case and doesn't cause any regressions. But, I'd like to hear back from Howard and Russell before going through with it on cxx0x-branch.
BTW, http://gcc.gnu.org/projects/cxx0x.html does not yet mention the rvalue reference patch.
We're waiting on one little copyright assignment detail. - Doug 2007-05-08 Douglas Gregor <[EMAIL PROTECTED]> * g++.dg/cpp0x/rvo.C: New. 2007-05-08 Douglas Gregor <[EMAIL PROTECTED]> * decl.c (move_fn_p): New. * call.c (build_over_call): In C++0x mode, we can elide move constructors as well as copy constructors. * cp-tree.h (DECL_MOVE_CONSTRUCTOR_P): New. (move_fn_p): Declare. Index: gcc/testsuite/g++.dg/cpp0x/rvo.C =================================================================== --- gcc/testsuite/g++.dg/cpp0x/rvo.C (revision 0) +++ gcc/testsuite/g++.dg/cpp0x/rvo.C (revision 0) @@ -0,0 +1,25 @@ +// { dg-do "run" } +// { dg-options "-std=c++0x" } +// Contributed by Sylvain Pion +static int rvalue_constructions = 0; + +struct A { + A () { } + A (const A&) { } + A (A&&) { ++rvalue_constructions; } + ~A () { } +}; + +A f() { return A(); } + +extern "C" { + void abort(void); +} + +int main() +{ + A c = f(); + + if (rvalue_constructions != 0) + abort(); +} Index: gcc/cp/decl.c =================================================================== --- gcc/cp/decl.c (revision 534) +++ gcc/cp/decl.c (working copy) @@ -9242,6 +9242,53 @@ copy_fn_p (tree d) return result; } +/* D is a constructor or overloaded `operator='. + + Let T be the class in which D is declared. Then, this function + returns true when D is a move constructor or move assignment + operator, false otherwise. */ + +bool +move_fn_p (tree d) +{ + tree args; + tree arg_type; + bool result = false; + + gcc_assert (DECL_FUNCTION_MEMBER_P (d)); + + if (TREE_CODE (d) == TEMPLATE_DECL + || (DECL_TEMPLATE_INFO (d) + && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (d)))) + /* Instantiations of template member functions are never copy + functions. Note that member functions of templated classes are + represented as template functions internally, and we must + accept those as copy functions. */ + return 0; + + args = FUNCTION_FIRST_USER_PARMTYPE (d); + if (!args) + return 0; + + arg_type = TREE_VALUE (args); + if (arg_type == error_mark_node) + return 0; + + if (TREE_CODE (arg_type) == REFERENCE_TYPE + && TYPE_REF_IS_RVALUE (arg_type) + && same_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (arg_type)), + DECL_CONTEXT (d))) + result = true; + + args = TREE_CHAIN (args); + + if (args && args != void_list_node && !TREE_PURPOSE (args)) + /* There are more non-optional args. */ + return false; + + return result; +} + /* Remember any special properties of member function DECL. */ void grok_special_member_properties (tree decl) Index: gcc/cp/call.c =================================================================== --- gcc/cp/call.c (revision 534) +++ gcc/cp/call.c (working copy) @@ -5078,7 +5078,9 @@ build_over_call (struct z_candidate *can if (! flag_elide_constructors) /* Do things the hard way. */; - else if (cand->num_convs == 1 && DECL_COPY_CONSTRUCTOR_P (fn)) + else if (cand->num_convs == 1 + && (DECL_COPY_CONSTRUCTOR_P (fn) + || (flag_cpp0x && DECL_MOVE_CONSTRUCTOR_P (fn)))) { tree targ; arg = argarray[num_artificial_parms_for (fn)]; Index: gcc/cp/cp-tree.h =================================================================== --- gcc/cp/cp-tree.h (revision 534) +++ gcc/cp/cp-tree.h (working copy) @@ -1871,6 +1871,10 @@ struct lang_decl GTY(()) #define DECL_COPY_CONSTRUCTOR_P(NODE) \ (DECL_CONSTRUCTOR_P (NODE) && copy_fn_p (NODE) > 0) +/* Nonzero if NODE (a FUNCTION_DECL) is a move constructor. */ +#define DECL_MOVE_CONSTRUCTOR_P(NODE) \ + (DECL_CONSTRUCTOR_P (NODE) && move_fn_p (NODE)) + /* Nonzero if NODE is a destructor. */ #define DECL_DESTRUCTOR_P(NODE) \ (DECL_LANG_SPECIFIC (NODE)->decl_flags.destructor_attr) @@ -4460,6 +4464,7 @@ extern tree build_ptrmem_type (tree, t /* the grokdeclarator prototype is in decl.h */ extern tree build_this_parm (tree, cp_cv_quals); extern int copy_fn_p (tree); +extern bool move_fn_p (tree); extern tree get_scope_of_declarator (const cp_declarator *); extern void grok_special_member_properties (tree); extern int grok_ctor_properties (tree, tree);