C++11 rvalue reference question

Discussion in 'C++' started by Andrew Tomazos, Dec 5, 2011.

  1. class O {...};
    class I {...};
    class E {...};

    Suppose we have some function f that takes an instance of class I as
    input and produces an instance of class O as output (throwing an
    instance of class E on an error):

    O f(const I& i)
    {
    O result;
    ... // mutate result based on i
    if (...)
    throw E(...);

    return result;
    }

    I i = ...;
    O o = f(i);

    In C++03 many times it was recommend to write such a function as
    follows:

    void f(O& out, const I& i)
    {
    .... // mutate result based on i
    if (,,,)
    throw E(...);
    }

    I i = ...;
    O o;
    f(o,i);

    I suspect this is to avoid the (potentially expensive) copy
    construction as the temporary O instance is copied from f's stack
    frame to the callers.

    The downside is that you then can't do things like:

    f(i).callSomeOFunction()

    or

    someFunctionTakingAnO(f(i))

    My question is with the advent of move constructors, xvalues and
    rvalue references in C++11, is there anyway to address this issue?

    What would this mean/do:

    O&& f(const I& i)
    {
    O result;
    ... // mutate result based on i
    if (...)
    throw E(...);

    return result; // or std::forward(result) ?
    }

    or is it broken?

    Thanks,
    Andrew.


    --
    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
    Andrew Tomazos, Dec 5, 2011
    #1
    1. Advertising

  2. On 2011-12-05 06:38, Andrew Tomazos wrote:
    > class O {...};
    > class I {...};
    > class E {...};
    >
    > Suppose we have some function f that takes an instance of class I as
    > input and produces an instance of class O as output (throwing an
    > instance of class E on an error):
    >
    > O f(const I& i)
    > {
    > O result;
    > ... // mutate result based on i
    > if (...)
    > throw E(...);
    >
    > return result;
    > }


    OK, so far.

    > I i = ...;
    > O o = f(i);
    >
    > In C++03 many times it was recommend to write such a function as
    > follows:
    >
    > void f(O& out, const I& i)
    > {
    > .... // mutate result based on i
    > if (,,,)
    > throw E(...);
    > }
    >
    > I i = ...;
    > O o;
    > f(o,i);
    >
    > I suspect this is to avoid the (potentially expensive) copy
    > construction as the temporary O instance is copied from f's stack
    > frame to the callers.
    > The downside is that you then can't do things like:
    >
    > f(i).callSomeOFunction()
    >
    > or
    >
    > someFunctionTakingAnO(f(i))


    Yes.

    > My question is with the advent of move constructors, xvalues and
    > rvalue references in C++11, is there anyway to address this issue?
    >
    > What would this mean/do:
    >
    > O&& f(const I& i)
    > {
    > O result;
    > ... // mutate result based on i
    > if (...)
    > throw E(...);
    >
    > return result; // or std::forward(result) ?
    > }
    >
    > or is it broken?


    This *is* broken, for the simple reason that this will produce a
    "dangling" reference to an automatic object that has been locally
    created in the function and that has been destroyed before the function
    call ends. Don't do that!

    Make it simple, just use return type O (without cv-qualifiers). The
    language has intentionally been adapted to enforce the compiler to first
    check whether the local object "result" can be be provided as an rvalue
    to the return statement to enable move semantics if the type provides some.

    HTH & Greetings from Bremen,

    Daniel Krügler


    --
    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
    Daniel Krügler, Dec 5, 2011
    #2
    1. Advertising

  3. Andrew Tomazos

    Arne L Guest

    On Dec 5, 6:38 am, Andrew Tomazos <> wrote:
    > ...
    > My question is with the advent of move constructors, xvalues and
    > rvalue references in C++11, is there anyway to address this issue?
    >
    > What would this mean/do:
    >
    > O&& f(const I& i)
    > {
    > O result;
    > ... // mutate result based on i
    > if (...)
    > throw E(...);
    >
    > return result; // or std::forward(result) ?
    >
    > }
    >
    > or is it broken?


    That is broken. Never return rvalue references from functions.
    Return by value instead. Even in C++98, there will be no
    temporary in almost all cases. Search for "return value
    optimization" and "named return value optimization".

    rvalue references are primarily useful in the "perfect
    forwarding" scenario, as well as during construction/
    assignment of objects from function return values.

    I i; // default constructor need be defined
    O o = f(i);

    In the second line, if RVO and NRVO fail, C++98
    will call the copy constructor, meaning copying
    of a temporary. In C++11, the move constructor
    will be invoked (if it is defined), meaning NO
    temporary will be copied. Note that RVO and
    NRVO are frequently applicable (and you need
    to do nothing to profit from it), so move
    assignment may be more commonly profitable:

    I i;
    O o;
    o = f(i);

    In C++98, this will create a temporary in line 3,
    neccessitating copying. In C++11, the move
    assignment operator will be called (if it is defined),
    meaning no copying will be performed.

    In short: use return by value generously. Classes
    from the standard library implement move semantics,
    so don't be afraid to return them by value;

    If you are writing your own classed (which own
    resources), consider giving them move construction
    and move assignment operator functions (as described
    elsewhere).

    Note that, generally, if you define one of the "big 5"
    (destructor, copy ctor, assignment operator, move
    ctor, move assignment operator), you will need to
    define all 5. (Previously called the Rule Of The Big Three).

    Greetings
    Arne Luenser


    --
    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
    Arne L, Dec 5, 2011
    #3
  4. on Mon Dec 05 2011, Andrew Tomazos <andrew-AT-tomazos.com> wrote:

    > In C++03 many times it was recommend to write such a function as
    > follows:
    >
    > void f(O& out, const I& i)
    > {
    > .... // mutate result based on i
    > if (,,,)
    > throw E(...);
    > }
    >
    > I i = ...;
    > O o;
    > f(o,i);
    >
    > I suspect this is to avoid the (potentially expensive) copy
    > construction as the temporary O instance is copied from f's stack
    > frame to the callers.


    Probably, but the code above has always been a bad idea even in C++03.
    Every compiler implements copy elision, so

    O o = f(i);

    is bound to be just as efficient, if not more so.

    http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

    Cheers,

    --
    Dave Abrahams
    BoostPro Computing
    http://www.boostpro.com


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
    Dave Abrahams, Dec 6, 2011
    #4
  5. Andrew Tomazos

    SG Guest

    On 6 Dez., 00:51, Arne L wrote:
    > On Dec 5, 6:38 am, Andrew Tomazos wrote:
    > > ...
    > > My question is with the advent of move constructors, xvalues and
    > > rvalue references in C++11, is there anyway to address this issue?


    Yes. Make your type movable without using any && outside of move ctors
    and move assignment operators. && is NOT some kind of compiler magic
    that makes anything movable. It's there to ALLOW you to write MOVABLE
    types.

    > [...]
    > Note that, generally, if you define one of the "big 5"
    > (destructor, copy ctor, assignment operator, move
    > ctor, move assignment operator), you will need to
    > define all 5. (Previously called the Rule Of The Big Three).


    No, it's not *that* bad. The rule of three still remains the rule of
    three, basically. And if it weren't for a little exception (C++03
    compatibility) to a really simple new rule (about compiler-generated
    copy/move operations), we wouldn't even need the rule of three
    anymore. Luckily, this exception is deprecated which is why we now can
    expect compilers to warn about rule-of-three violations. The simple
    rule I was referring to earlier is:

    - If you define any of the 5, the compiler won't generate any
    other copy/move operations.

    Obviously this is not compatible with existing C++03 code. So, unless
    you declare a custom move operation (ctor or assignment or both) we're
    back to C++03 rules w.r.t. compiler-generated operations.

    So, if we put this together we'll get:

    - user-declared move operations inhibit the generation of
    all other copy/move operations.

    - user-declared dtor or copy operations inhibit the
    generation of all other move operations
    (but not yet the remaining copy operations due to
    C++03 compatibility)

    I think it's safe to say that the situation actually improved.

    Cheers!
    SG


    --
    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
    SG, Dec 6, 2011
    #5
  6. Andrew Tomazos

    Ebenezer Guest

    On Dec 6, 1:59 pm, Dave Abrahams <> wrote:
    > on Mon Dec 05 2011, Andrew Tomazos <andrew-AT-tomazos.com> wrote:
    > > In C++03 many times it was recommend to write such a function as
    > > follows:

    >
    > > void f(O& out, const I& i)
    > > {
    > > .... // mutate result based on i
    > > if (,,,)
    > > throw E(...);
    > > }

    >
    > > I i = ...;
    > > O o;
    > > f(o,i);

    >
    > > I suspect this is to avoid the (potentially expensive) copy
    > > construction as the temporary O instance is copied from f's stack
    > > frame to the callers.

    >
    > Probably, but the code above has always been a bad idea even in C++03.
    > Every compiler implements copy elision, so
    >
    > O o = f(i);
    >
    > is bound to be just as efficient, if not more so.
    >


    I agree that's nice, but in a marshalling context, the other
    form is able to handle multiple outputs better

    O1 o1;
    O2 o2;
    Msgs.Receive(buf, o1, o2);


    Brian Wood
    Ebenezer Enterprises
    http://webEbenezer.net


    --
    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
    Ebenezer, Dec 11, 2011
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Kavya
    Replies:
    9
    Views:
    508
    Dik T. Winter
    Oct 28, 2006
  2. Replies:
    1
    Views:
    412
    Jerry Coffin
    May 10, 2008
  3. Nephi Immortal
    Replies:
    1
    Views:
    612
  4. dervih
    Replies:
    3
    Views:
    504
    dervih
    Jul 13, 2012
  5. Juha Nieminen
    Replies:
    13
    Views:
    613
    Edek Pienkowski
    Aug 29, 2012
Loading...

Share This Page