Returning const objects?

Discussion in 'C++' started by Derek, Jan 6, 2005.

  1. Derek

    Derek Guest

    Some authors advocate returning const objects:

    const Point operator+(const Point&, const Point&);
    ^^^^^

    Returning a const object prevents some bad code from compiling:

    Point a, b, c;
    (a + b) = c; // error

    But it also prevents some potentially convenient constructs:

    Point a, b, c;
    a = (b + c).normalize(); // error

    The best argument I've heard to settle this standoff is that
    returning const objects makes them behave like built-in types:

    int x, y, z, w;
    x = (y + z) *= w; // error

    Are there any other compelling reasons to adopt/ignore this
    convention?
     
    Derek, Jan 6, 2005
    #1
    1. Advertising

  2. Derek wrote:
    > Some authors advocate returning const objects:
    >
    > const Point operator+(const Point&, const Point&);
    > ^^^^^
    >
    > Returning a const object prevents some bad code from compiling:
    >
    > Point a, b, c;
    > (a + b) = c; // error
    >
    > But it also prevents some potentially convenient constructs:
    >
    > Point a, b, c;
    > a = (b + c).normalize(); // error


    What's stopping you from creating a "const" version of normalize that
    returns a copy of a normalized Point ?

    i.e.

    class Point
    {
    ..... blah ....

    Point & normalize();
    const Point normalize() const;
    };

    >
    > The best argument I've heard to settle this standoff is that
    > returning const objects makes them behave like built-in types:
    >
    > int x, y, z, w;
    > x = (y + z) *= w; // error
    >
    > Are there any other compelling reasons to adopt/ignore this
    > convention?
     
    Gianni Mariani, Jan 6, 2005
    #2
    1. Advertising

  3. Derek

    Howard Guest

    "Derek" <> wrote in message
    news:...
    > Some authors advocate returning const objects:
    >
    > const Point operator+(const Point&, const Point&);
    > ^^^^^
    >
    > Returning a const object prevents some bad code from compiling:
    >
    > Point a, b, c;
    > (a + b) = c; // error
    >
    > But it also prevents some potentially convenient constructs:
    >
    > Point a, b, c;
    > a = (b + c).normalize(); // error


    Or you can do it like I would:

    Point a(b+c);
    a.normalize();

    Less convenient? Maybe, but it's clear, and easy to debug. My rule: do one
    thing at a time.

    -Howard
     
    Howard, Jan 6, 2005
    #3
  4. In article <>, Derek <>
    wrote:

    > Some authors advocate returning const objects:
    >
    > const Point operator+(const Point&, const Point&);
    > ^^^^^
    >
    > Returning a const object prevents some bad code from compiling:
    >
    > Point a, b, c;
    > (a + b) = c; // error
    >
    > But it also prevents some potentially convenient constructs:
    >
    > Point a, b, c;
    > a = (b + c).normalize(); // error
    >
    > The best argument I've heard to settle this standoff is that
    > returning const objects makes them behave like built-in types:
    >
    > int x, y, z, w;
    > x = (y + z) *= w; // error
    >
    > Are there any other compelling reasons to adopt/ignore this
    > convention?


    My vote is to ignore this convention. Good conventions prevent
    /accidental/ errors. When was the last time you saw something like this
    happen by accident:

    (a + b) = c; // error

    ?!

    I've never seen it happen. Otoh, I have personally used constructs like:

    a = (b + c).normalize();

    and would be frustrated if the return of op+ forced me to make a useless
    copy:

    a = Point(b + c).normalize();

    It would be more fun to just insert sleep cycles in my code. :-\

    Looking at the evolution of C++, I believe we will see move semantics in
    C++0X (I'm currently working very hard to make that happen). And const
    return types will defeat the tremendous performance optimizations made
    by move semantics.

    Reference:

    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html

    Consider:

    std::vector<int>
    foo(unsigned n)
    {
    return std::vector<int>(n);
    }

    int main()
    {
    std::vector<int> v;
    v = foo(3);
    }

    Today this code will go to the heap at least twice:

    1. To create the vector inside of foo.
    2. To assign from foo's return to v.

    On some compilers you may actually go to the heap 3 times (if RVO isn't
    implemented).

    If/when move semantics comes to a compiler near you, the above program
    will go to the heap only once, whether or not RVO is implemented.

    1. To create the vector inside of foo.

    The assignment will be a "move assignment", transferring resources from
    the temporary (foo's return) to v. Any copies that are not elided away
    in the call to foo will use vector's move constructor instead of
    vector's copy constructor.

    However if you would like to completely disable this optimization which
    move semantics will give you, simply code:

    const std::vector<int>
    foo(unsigned n)
    {
    return std::vector<int>(n);
    }

    More sleep cycles anyone? ;-)

    Although you can't take advantage of move semantics today, I believe you
    will in the future. Coding const return values today is just planting
    silent performance killers in tomorrow's code.

    -Howard
     
    Howard Hinnant, Jan 6, 2005
    #4
  5. Derek

    Rolf Magnus Guest

    Gianni Mariani wrote:

    > Derek wrote:
    >> Some authors advocate returning const objects:
    >>
    >> const Point operator+(const Point&, const Point&);
    >> ^^^^^
    >>
    >> Returning a const object prevents some bad code from compiling:
    >>
    >> Point a, b, c;
    >> (a + b) = c; // error
    >>
    >> But it also prevents some potentially convenient constructs:
    >>
    >> Point a, b, c;
    >> a = (b + c).normalize(); // error

    >
    > What's stopping you from creating a "const" version of normalize that
    > returns a copy of a normalized Point ?
    >
    > i.e.
    >
    > class Point
    > {
    > .... blah ....
    >
    > Point & normalize();
    > const Point normalize() const;
    > };


    You mean a non-const version that modifies the Point itself and a const
    version that copies it and modifies the copy? Sounds quite unintuitive to
    me. If you try to normalize a Point and didn't notice that that point is
    const, your operation is just silently ignored.
     
    Rolf Magnus, Jan 6, 2005
    #5
  6. Derek

    Rolf Magnus Guest

    Howard Hinnant wrote:

    > My vote is to ignore this convention. Good conventions prevent
    > /accidental/ errors. When was the last time you saw something like this
    > happen by accident:
    >
    > (a + b) = c; // error
    >
    > ?!
    >
    > I've never seen it happen. Otoh, I have personally used constructs like:
    >
    > a = (b + c).normalize();
    >
    > and would be frustrated if the return of op+ forced me to make a useless
    > copy:
    >
    > a = Point(b + c).normalize();
    >
    > It would be more fun to just insert sleep cycles in my code. :-\


    Actually, since you talk about additional copies: I would let functions like
    normalize modify the object they were called for instead of making a copy
    and then modifying that copy. If the user of the class wants the original
    object to be modified, he will have to copy it back, like:

    a = a.normalize();

    Better let the user decide when he wants to copy the object.
     
    Rolf Magnus, Jan 6, 2005
    #6
  7. "Derek" <> wrote in message
    news:...
    > Some authors advocate returning const objects:
    >
    > const Point operator+(const Point&, const Point&);
    > ^^^^^
    >
    > Returning a const object prevents some bad code from compiling:
    >
    > Point a, b, c;
    > (a + b) = c; // error


    But the real question is: have you seen such an error
    happen inadvertedly, or cause any bugs?
    Does anyone have an example of an accidental error that
    would have been prevented by returning a const type?

    > But it also prevents some potentially convenient constructs:
    >
    > Point a, b, c;
    > a = (b + c).normalize(); // error


    With the vector library I use, I would write anyway:
    a = unit(b+c); // works regardless...
    I also have a 'normalize()' member function returning void,
    but I do not like to mix mutating calls with accessor functions.

    > The best argument I've heard to settle this standoff is that
    > returning const objects makes them behave like built-in types:
    >
    > int x, y, z, w;
    > x = (y + z) *= w; // error
    >
    > Are there any other compelling reasons to adopt/ignore this
    > convention?


    I'm fine regardless, but I would not bother using the more
    verbose syntax if it cannot at least prevent some kind of
    accidental error. In this case, it doesn't seem worth
    the effort IMHO.

    Cheers,
    Ivan
    --
    http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form
     
    Ivan Vecerina, Jan 6, 2005
    #7
  8. Rolf Magnus wrote:
    > Gianni Mariani wrote:
    >


    ....

    >
    > You mean a non-const version that modifies the Point itself and a const
    > version that copies it and modifies the copy? Sounds quite unintuitive to
    > me. If you try to normalize a Point and didn't notice that that point is
    > const, your operation is just silently ignored.


    Good point. (no pun intended) The idea in the previous post is dangerous.

    Come to think of it, the most intuitive thing is that normalize returns
    a copy of a normalized object. The whole idea of normalize is not too
    different to unary minus which makes a copy. Another idea is to have 2
    methods, normalize and normalize_self where normalize returns a copy and
    normalize_self operates on the object itself.

    G
     
    Gianni Mariani, Jan 7, 2005
    #8
  9. Gianni Mariani wrote:
    > [..]
    > Come to think of it, the most intuitive thing is that normalize returns
    > a copy of a normalized object. The whole idea of normalize is not too
    > different to unary minus which makes a copy. Another idea is to have 2
    > methods, normalize and normalize_self where normalize returns a copy and
    > normalize_self operates on the object itself.


    I'd say the const member should probably be named "get_normalized_copy" or
    something the like. That's where style guides are often helpful. Longer
    names should be used for functions that are used less often, probably. But
    that's just IMHO.

    V
     
    Victor Bazarov, Jan 7, 2005
    #9
    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. Replies:
    11
    Views:
    1,109
  2. Javier
    Replies:
    2
    Views:
    567
    James Kanze
    Sep 4, 2007
  3. 0m
    Replies:
    26
    Views:
    1,123
    Tim Rentsch
    Nov 10, 2008
  4. fungus
    Replies:
    13
    Views:
    892
    fungus
    Oct 31, 2008
  5. Jens Thoms Toerring

    Const/non-const pointer returning method

    Jens Thoms Toerring, May 25, 2010, in forum: C++
    Replies:
    11
    Views:
    724
    John H.
    May 26, 2010
Loading...

Share This Page