Returning const objects?

D

Derek

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?
 
G

Gianni Mariani

Derek said:
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;
};
 
H

Howard

Derek said:
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
 
H

Howard Hinnant

Derek <[email protected]> said:
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
 
R

Rolf Magnus

Gianni said:
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.
 
R

Rolf Magnus

Howard said:
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.
 
I

Ivan Vecerina

Derek said:
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
 
G

Gianni Mariani

Rolf said:
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
 
V

Victor Bazarov

Gianni said:
[..]
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,197
Messages
2,571,040
Members
47,634
Latest member
RonnyBoelk

Latest Threads

Top