Topic: Any better way for std::move and std::forward usage ?


Author: ujjwal<ujjwal.rp@gmail.com>
Date: Tue, 1 Nov 2011 11:15:59 -0700 (PDT)
Raw View
Hi,

I am trying to understand move symantics in c++0x. I have a small
program which illustrates the cases when move constructor should be
called.

I am looking for better ways to eliminate code duplication -

e.g. I have assign function which reassigns the member variables.
(Instead of a separate function I could have used deligated
constructors but my VC version does not support yet.)

I use this function in copy, move constructors and assignment
operators.


template<typename T1, typename T2, typename T3>
  void assign(T1&&  id, T2&&  name, T3&&  address)
 {
  _id = std::forward<T1>(id);
  _name = std::forward<T2>(name);
  _address = std::forward<T3>(address);
 }


Q : Why do I need to call std::move explicitely ? Why e._id does not
resolve to&&  as e is passed by&&



Employee(Employee&&  e)
 {
  std::cout<<  "Employee::Employee Move"<<  std::endl;
  /**
   * This version VC10 does not support delegating constructors.
   */
  assign(std::move(e._id), std::move(e._name),
std::move(e._address));
 }


Cheers
Ujjwal


Sample program -

#include "stdafx.h"
#include<iostream>
#include<string>


class Address
{
public:
 Address()
 {}

 Address(const Address&  a)
 {
  std::cout<<  "Address::Address Copy"<<  std::endl;
 }

 Address&  operator = (const Address&  a)
 {
  std::cout<<  "Address::operator = Copy"<<  std::endl;
  return *this;
 }

 Address(Address&&  a)
 {
  std::cout<<  "Address::Address Move"<<  std::endl;
 }

 Address&  operator = (Address&&  a)
 {
  std::cout<<  "Address::operator = Move"<<  std::endl;
  return *this;
 }
};

class Employee
{
public:

 /*
 template<typename T1, typename T2, typename T3>
 Employee(T1&&  id, T2&&  name, T3&&  address)
  : _id(std::forward<T1>(id))
  , _name(std::forward<T2>(name))
  , _address(std::forward<T3>(address))
 {}
 */

     /**
  * @note This version VC10 does not support delegating constructors.
We could have used it directly.
  */
 template<typename T1, typename T2, typename T3>
 void assign(T1&&  id, T2&&  name, T3&&  address)
 {
  _id = std::forward<T1>(id);
  _name = std::forward<T2>(name);
  _address = std::forward<T3>(address);
 }

 template<typename T1, typename T2, typename T3>
 Employee(T1&&  id, T2&&  name, T3&&  address)
 {
  assign(id, name, address);
 }

 Employee(Employee&&  e)
 {
  std::cout<<  "Employee::Employee Move"<<  std::endl;
  /**
   * This version VC10 does not support delegating constructors.
   */
  assign(std::move(e._id), std::move(e._name),
std::move(e._address));  ///@todo - Why do I need to call std::move
explicitely ? Why e._id does not resolve to&&  as e is passed by&&
 }


 Employee&  operator = (Employee&&  e)
 {
  std::cout<<  "Employee::Operator = Move"<<  std::endl;
  assign(std::move(e._id), std::move(e._name),
std::move(e._address)); ///@todo - Why do I need to call std::move
explicitely ? Why e._id does not resolve to&&  as e is passed by&&
  return *this;
 }


 Employee(const Employee&  e)
 {
  std::cout<<  "Employee::Employee Copy"<<  std::endl;
  assign(e._id, e._name, e._address);

 }

 Employee&  operator = (const Employee&  e)
 {
  std::cout<<  "Employee::Operator =  Copy"<<  std::endl;
  assign(e._id, e._name, e._address);
  return *this;
 }

 ///@note instead of repeating the code for all 4 types of functions
which do similar work, is there any smart idea like std::forward until
following is available ?
 /**
  * Employee&  operator = (Employee&&  e) = default;
  * Employee(Employee&&  e) = default
  * section 8.4.2 c++11 iso standards
  */



private:
 int _id;
 std::string _name;
 Address _address;
};



int _tmain(int argc, _TCHAR* argv[])
{


 auto make_employee = [](int id, const std::string&  name, const
Address&  address) ->  Employee
       {
        Employee e(id, name, address);
        return e;
       };


     Address addr;

 std::cout<<  "\n\nmake_employee(1, \"Ujjwal\", addr)"<<  std::endl;
 Employee e1 = make_employee(1, "Ujjwal", addr);
 /**
  * Address::operator = Copy
  * Employee::Employee Move
  * Address::operator = Move
  */
 std::cout<<  std::endl<<  std::endl;

 std::cout<<  "Employee e2(e1)"<<  std::endl;
 Employee e2(e1);
 /**
  * Employee::Employee Copy
  * Address::operator = Copy
  */
 std::cout<<  std::endl<<  std::endl;


 std::cout<<  "Employee e3 = std::move(e2)"<<  std::endl;
 ///@note - Don't see any practical use, but just to study move
behaviour.
 Employee e3 = std::move(e2);
 /**
  * Employee::Employee Move
  * Address::operator = Move
  */
 std::cout<<  std::endl<<  std::endl;

 return 0;
}


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]




Author: Howard Hinnant<howard.hinnant@gmail.com>
Date: Wed, 2 Nov 2011 11:15:25 -0700 (PDT)
Raw View
On Nov 1, 2:15 pm, ujjwal<ujjwal...@gmail.com>  wrote:

>  Q : Why do I need to call std::move explicitely ? Why e._id does not
>  resolve to&&    as e is passed by&&
>
>  Employee(Employee&&    e)
>           {
>                   std::cout<<    "Employee::Employee Move"<<    std::endl;
>                   /**
>                    * This version VC10 does not support delegating constructors.
>                    */
>                   assign(std::move(e._id), std::move(e._name),
>  std::move(e._address));
>           }

This was done to eliminate accidental moves when the named object is
used multiple times in a block of code.  Here is the original paper
discussing this point:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1377.htm#More%20on%20A&&

Start about half way through that section, where it says:

>  Even though named rvalue references...


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]




Author: "Kevin P. Fleming"<news@kpfleming.us>
Date: Wed, 2 Nov 2011 11:15:56 -0700 (PDT)
Raw View
On 11/01/2011 01:15 PM, ujjwal wrote:
>  Hi,
>
>  I am trying to understand move symantics in c++0x. I have a small
>  program which illustrates the cases when move constructor should be
>  called.
>
>  I am looking for better ways to eliminate code duplication -
>
>  e.g. I have assign function which reassigns the member variables.
>  (Instead of a separate function I could have used deligated
>  constructors but my VC version does not support yet.)
>
>  I use this function in copy, move constructors and assignment
>  operators.
>
>
>  template<typename T1, typename T2, typename T3>
>     void assign(T1&&    id, T2&&    name, T3&&    address)
>   {
>    _id = std::forward<T1>(id);
>    _name = std::forward<T2>(name);
>    _address = std::forward<T3>(address);
>   }
>
>
>  Q : Why do I need to call std::move explicitely ? Why e._id does not
>  resolve to&&    as e is passed by&&

The '&&' modifier has a different meaning for template function
parameters than it does for non-template function parameters.


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]




Author: SG <s.gesemann@gmail.com>
Date: Wed, 2 Nov 2011 11:17:30 -0700 (PDT)
Raw View
On 1 Nov., 19:15, wrote:
>
> I am trying to understand move symantics in c++0x. I have a small
> program which illustrates the cases when move constructor should be
> called.
>
> I am looking for better ways to eliminate code duplication -
>
> e.g. I have assign function which reassigns the member variables.
> (Instead of a separate function I could have used deligated
> constructors but my VC version does not support yet.)

If you want to eliminate code duplication w.r.t. copy/move operations,
the best bet is probably to rely on the compiler to write these
operations for you. That means, composing "larger" classes out of
smaller ones which already have the correct copy/move semantics. For
example, take this class:

  struct foo {
    string firstName;
    string lastName;
    int yearOfBirth;
  };

Here, you don't need to do anything w.r.t. copy/move operations
because the compiler is expected to be smart enough to do the right
thing in this case (member-wise copy/move). If, for some reason, you
need to define your own copy/move semantics for some class, you should
limit your class' responsibility to managing at most one resource and
compose a larger class out of these smaller building blocks so that
you can let the compiler do these member-wise copying/moving of all
the members automatically.

So, if you feel the need to define lots of copy/move ctors, assignment
operators and destructors for almost any class, then you definitely
did something wrong.

> I use this function in copy, move constructors and assignment
> operators.
>
>   template<typename T1, typename T2, typename T3>
>   void assign(T1&&  id, T2&&  name, T3&&  address)
>   {
>     _id = std::forward<T1>(id);
>     _name = std::forward<T2>(name);
>     _address = std::forward<T3>(address);
>   }
>
> Q : Why do I need to call std::move explicitely ? Why e._id does not
> resolve to&&  as e is passed by&&
>
> Employee(Employee&&  e)
> {
>   std::cout<<  "Employee::Employee Move"<<  std::endl;
>   assign(std::move(e._id),
>          std::move(e._name),
>          std::move(e._address));
> }

This is intentional. Once, you initialized a *named* rvalue reference
to refer to some object you have the chance to refer to this object
*multiple* times via this new name. This is characteristic of
*lvalues*. So, the *name* of an rvalue reference makes an lvalue
expression. This is to prevent implicit/accidental moving. It may seem
odd at first, but it makes a lot of sense, actually.

Cheers!
SG


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]




Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <daniel.kruegler@googlemail.com>
Date: Wed, 2 Nov 2011 11:20:34 -0700 (PDT)
Raw View
Am 01.11.2011 19:15, schrieb ujjwal:
> I am trying to understand move symantics in c++0x. I have a small
> program which illustrates the cases when move constructor should be
> called.

Let me begin with the remark, that for understanding move semantics and
"perfect forwarding" functions you really need to understand the
concepts of value categories in C++. For the level needed here, it is
sufficient to distinguish the classical two value categories, lvalues
and rvalues, where the former refer to some nameable object (or
function) with a memory identity and the latter refer to an (unnamed)
temporary object or non-object value (like a literal such as 'true' or
'42').

> I am looking for better ways to eliminate code duplication -
>
> e.g. I have assign function which reassigns the member variables.
> (Instead of a separate function I could have used deligated
> constructors but my VC version does not support yet.)
>
> I use this function in copy, move constructors and assignment
> operators.
>
> template<typename T1, typename T2, typename T3>
>    void assign(T1&&   id, T2&&   name, T3&&   address)
>  {
>   _id = std::forward<T1>(id);
>   _name = std::forward<T2>(name);
>   _address = std::forward<T3>(address);
>  }
>
> Q : Why do I need to call std::move explicitely ?

I do not see any std::move here and you don't need it: You are defining
here a "perfect forwarding" signature here. This means that the actually
deduced type for any argument reflects the value category of this
argument. Any lvalue will deduce to some lvalue-reference type Ti
(i=1,2,3), any rvalue will deduce to a non-reference type Ti. If you
would directly refer to the names id, name, or address, these are always
lvalues (and you would loose the information contained within the
deduced types), because they are "named" variables. To conserve the
deduced value, category, std::forward is used here. It has the effect of
std::move, if the value was an rvalue, else it is just an "identity"
transfer.

> Why e._id does not
> resolve to&&   as e is passed by&&
>
> Employee(Employee&&   e)
>  {
>   std::cout<<   "Employee::Employee Move"<<   std::endl;
>   /**
>    * This version VC10 does not support delegating constructors.
>    */
>   assign(std::move(e._id), std::move(e._name),
> std::move(e._address));
>  }

Your original presentation let me read you meant your assign function
above, but the latter text makes only sense, if you intended to refer to
this constructor. But we can borrow one conclusion from above: Since e
is a named variable, this is an lvalue. Note that types or type
categories (like lvalue-reference or rvalue-reference) are generally
orthogonal to value categories. Thus by recognicing an rvalue-reference,
this doesn't automatically mean that the value category of this thing is
an rvalue. First, all function rvalue references are lvalues, second,
any named rvalue reference is an lvalue. Don't try to mix type category
and value category, that doesn't work!
So, to ensure that the lvalues e._id, e._name, and e._address are
properly *propagated* as rvalues to the assign template, you need
std::move here, because the expression std::move(obj) is an rvalue for
any expression obj that is an object type.

> class Employee
> {
> public:

[..]

>  template<typename T1, typename T2, typename T3>
>  void assign(T1&&   id, T2&&   name, T3&&   address)
>  {
>   _id = std::forward<T1>(id);
>   _name = std::forward<T2>(name);
>   _address = std::forward<T3>(address);
>  }
>
>  template<typename T1, typename T2, typename T3>
>  Employee(T1&&   id, T2&&   name, T3&&   address)
>  {
>   assign(id, name, address);
>  }

Here you need std::forward again. As written, every parameter - whether
it was *originally* an rvalue or an lvalue - will be transferred as
lvalue. Let me warn you, that replacing copy/move construction by
assignment does not work in all cases and may also perform unnecessary
additional work. In this example std::string and the Address member will
be default-initialized. I suggest to keep the original form.

>  Employee(Employee&&   e)
>  {
>   std::cout<<   "Employee::Employee Move"<<   std::endl;
>   assign(std::move(e._id), std::move(e._name),
> std::move(e._address));  ///@todo - Why do I need to call std::move
> explicitely ? Why e._id does not resolve to&&   as e is passed by&&
>  }

As explained above, the template deduction depends on the value category
and the type. e as a named variable is an lvalue, so are it's
subobjects. You need std::move to transform back to an rvalue again.
Actually this is similar to why you need std::forward in the
aforementioned constructor template.

If you want to get rid of unnecessary code, you could simple
user-default this member as

Employee(Employee&&) = default;

>  Employee&   operator = (Employee&&   e)
>  {
>   std::cout<<   "Employee::Operator = Move"<<   std::endl;
>   assign(std::move(e._id), std::move(e._name),
> std::move(e._address)); ///@todo - Why do I need to call std::move
> explicitely ? Why e._id does not resolve to&&   as e is passed by&&
>   return *this;
>  }

Same reason as above. To simplify, just user-default this member like so:

Employee& operator=(Employee&&) = default;

>  Employee&   operator = (const Employee&   e)
>  {
>   std::cout<<   "Employee::Operator =  Copy"<<   std::endl;
>   assign(e._id, e._name, e._address);
>   return *this;
>  }
>
>  ///@note instead of repeating the code for all 4 types of functions
> which do similar work, is there any smart idea like std::forward until
> following is available ?
>  /**
>   * Employee&   operator = (Employee&&   e) = default;
>   * Employee(Employee&&   e) = default
>   * section 8.4.2 c++11 iso standards
>   */

User-defaulting these members looks like the most reasonble approach:

Employee(const Employee&) = default;
Employee& operator=(const Employee&) = default;

HTH & Greetings from Bremen,

Daniel Kr   gler


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]




Author: ujjwal <ujjwal.rp@gmail.com>
Date: Thu, 3 Nov 2011 17:09:33 -0700 (PDT)
Raw View
On Nov 3, 2:20 am, Daniel Kr   gler <daniel.krueg...@googlemail.com>
wrote:
> Am 01.11.2011 19:15, schrieb ujjwal:
>
> > I am trying to understand move symantics in c++0x. I have a small
> > program which illustrates the cases when move constructor should be
> > called.
>
> Let me begin with the remark, that for understanding move semantics and
> "perfect forwarding" functions you really need to understand the
> concepts of value categories in C++. For the level needed here, it is
> sufficient to distinguish the classical two value categories, lvalues
> and rvalues, where the former refer to some nameable object (or
> function) with a memory identity and the latter refer to an (unnamed)
> temporary object or non-object value (like a literal such as 'true' or
> '42').
>
>
>
>
>
> > I am looking for better ways to eliminate code duplication -
>
> > e.g. I have assign function which reassigns the member variables.
> > (Instead of a separate function I could have used deligated
> > constructors but my VC version does not support yet.)
>
> > I use this function in copy, move constructors and assignment
> > operators.
>
> > template<typename T1, typename T2, typename T3>
> >    void assign(T1&&   id, T2&&   name, T3&&   address)
> >    {
> >            _id = std::forward<T1>(id);
> >            _name = std::forward<T2>(name);
> >            _address = std::forward<T3>(address);
> >    }
>
> > Q : Why do I need to call std::move explicitely ?
>
> I do not see any std::move here and you don't need it: You are defining
> here a "perfect forwarding" signature here. This means that the actually
> deduced type for any argument reflects the value category of this
> argument. Any lvalue will deduce to some lvalue-reference type Ti
> (i=1,2,3), any rvalue will deduce to a non-reference type Ti. If you
> would directly refer to the names id, name, or address, these are always
> lvalues (and you would loose the information contained within the
> deduced types), because they are "named" variables. To conserve the
> deduced value, category, std::forward is used here. It has the effect of
> std::move, if the value was an rvalue, else it is just an "identity"
> transfer.
>
> > Why e._id does not
> > resolve to&&   as e is passed by&&
>
> > Employee(Employee&&   e)
> >    {
> >            std::cout<<   "Employee::Employee Move"<<   std::endl;
> >            /**
> >             * This version VC10 does not support delegating constructors.
> >             */
> >            assign(std::move(e._id), std::move(e._name),
> > std::move(e._address));
> >    }
>
> Your original presentation let me read you meant your assign function
> above, but the latter text makes only sense, if you intended to refer to
> this constructor. But we can borrow one conclusion from above: Since e
> is a named variable, this is an lvalue. Note that types or type
> categories (like lvalue-reference or rvalue-reference) are generally
> orthogonal to value categories. Thus by recognicing an rvalue-reference,
> this doesn't automatically mean that the value category of this thing is
> an rvalue. First, all function rvalue references are lvalues, second,
> any named rvalue reference is an lvalue. Don't try to mix type category
> and value category, that doesn't work!
> So, to ensure that the lvalues e._id, e._name, and e._address are
> properly *propagated* as rvalues to the assign template, you need
> std::move here, because the expression std::move(obj) is an rvalue for
> any expression obj that is an object type.
>
> > class Employee
> > {
> > public:
>
> [..]
>
> >    template<typename T1, typename T2, typename T3>
> >    void assign(T1&&   id, T2&&   name, T3&&   address)
> >    {
> >            _id = std::forward<T1>(id);
> >            _name = std::forward<T2>(name);
> >            _address = std::forward<T3>(address);
> >    }
>
> >    template<typename T1, typename T2, typename T3>
> >    Employee(T1&&   id, T2&&   name, T3&&   address)
> >    {
> >            assign(id, name, address);
> >    }
>
> Here you need std::forward again. As written, every parameter - whether
> it was *originally* an rvalue or an lvalue - will be transferred as
> lvalue. Let me warn you, that replacing copy/move construction by
> assignment does not work in all cases and may also perform unnecessary
> additional work. In this example std::string and the Address member will
> be default-initialized. I suggest to keep the original form.
>
> >    Employee(Employee&&   e)
> >    {
> >            std::cout<<   "Employee::Employee Move"<<   std::endl;
> >            assign(std::move(e._id), std::move(e._name),
> > std::move(e._address));  ///@todo - Why do I need to call std::move
> > explicitely ? Why e._id does not resolve to&&   as e is passed by&&
> >    }
>
> As explained above, the template deduction depends on the value category
> and the type. e as a named variable is an lvalue, so are it's
> subobjects. You need std::move to transform back to an rvalue again.
> Actually this is similar to why you need std::forward in the
> aforementioned constructor template.
>
> If you want to get rid of unnecessary code, you could simple
> user-default this member as
>
> Employee(Employee&&) = default;
>
> >    Employee&   operator = (Employee&&   e)
> >    {
> >            std::cout<<   "Employee::Operator = Move"<<   std::endl;
> >            assign(std::move(e._id), std::move(e._name),
> > std::move(e._address)); ///@todo - Why do I need to call std::move
> > explicitely ? Why e._id does not resolve to&&   as e is passed by&&
> >            return *this;
> >    }
>
> Same reason as above. To simplify, just user-default this member like so:
>
> Employee& operator=(Employee&&) = default;
>
> >    Employee&   operator = (const Employee&   e)
> >    {
> >            std::cout<<   "Employee::Operator =  Copy"<<   std::endl;
> >            assign(e._id, e._name, e._address);
> >            return *this;
> >    }
>
> >    ///@note instead of repeating the code for all 4 types of functions
> > which do similar work, is there any smart idea like std::forward until
> > following is available ?
> >    /**
> >     * Employee&   operator = (Employee&&   e) = default;
> >     * Employee(Employee&&   e) = default
> >     * section 8.4.2 c++11 iso standards
> >     */
>
> User-defaulting these members looks like the most reasonble approach:
>
> Employee(const Employee&) = default;
> Employee& operator=(const Employee&) = default;
>
> HTH & Greetings from Bremen,
>
> Daniel Kr   gler
>
> --
> [ comp.std.c++ is moderated.  To submit articles, try posting with your ]
> [ newsreader.  If that fails, use mailto:std-cpp-sub...@vandevoorde.com ]
> [              --- Please see the FAQ before posting. ---               ]
> [ FAQ:http://www.comeaucomputing.com/csc/faq.html                     ]- Hide quoted text -
>
> - Show quoted text -


Thank you all for your responses.

Cheers,
Ujjwal


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]