Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

const & op+, 2 questions

1 view
Skip to first unread message

Matt Bitten

unread,
Dec 26, 2002, 12:02:24 AM12/26/02
to
Effective C++ suggests overloading operators so as to mimic the built-in
operators. Based on this, operator+ should not be a member function, so
that type conversion can be performed on the first operand, and it
should
be prototyped as

const Foo operator+(const Foo &, const Foo &);

I have two questions about this.

(1) I hear that some people say one should NEVER return const by value.
Can anyone explain why const-by-value might be a problem? Avoiding it
would clearly contradict the mimicking principle from EC++ mentioned
above, since built-in types returned by value are (effectively) always
const.

(2) I never thought about this before, but how can passing a const
reference allow for type conversion? I cannot think of any reasonable
intuitive meaning for a const reference ("A reference _is_ the object" -
from Cline's FAQ, 8.1) nor any reasonable way to implement it ("A
reference is a pointer in disguise") that allows for type conversion.

-- Matt

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Joshua Lehrer

unread,
Dec 26, 2002, 12:58:00 PM12/26/02
to
mbit...@yahoo.com (Matt Bitten) wrote in message
news:<f865ded1.02122...@posting.google.com>...

> const Foo operator+(const Foo &, const Foo &);
>
> I have two questions about this.
>
> (1) I hear that some people say one should NEVER return const by value

I'm not one of those people, so I can't say.

>
> (2) I never thought about this before, but how can passing a const
> reference allow for type conversion?

void func(const int & value);
func(3.5);

The float "3.5" is converted to a temporary integer "3". A
const-reference is bound to that temporary, and passed into func.
When the function exits, the reference is dropped, and the temporary
falls out of scope and is destroyed.

joshua lehrer
factset research systems

Carlos Moreno

unread,
Dec 26, 2002, 1:00:52 PM12/26/02
to
Matt Bitten wrote:
> Effective C++ suggests overloading operators so as to mimic the
> built-in
> operators. Based on this, operator+ should not be a member function, so
> that type conversion can be performed on the first operand, and it
> should
> be prototyped as
>
> const Foo operator+(const Foo &, const Foo &);
>
> I have two questions about this.
>
> (1) I hear that some people say one should NEVER return const by value.

Who said that? They're undoubtedly wrong. In a case like this,
you *should* return by value, with const qualification -- not that
catastrophe will happen if you don't; it's just that, as you said,
you want to mimic the behaviour of built-ins, which would not
return a modifiable rvalue.

> (2) I never thought about this before, but how can passing a const
> reference allow for type conversion? I cannot think of any reasonable
> intuitive meaning for a const reference ("A reference _is_ the object"
> -
> from Cline's FAQ, 8.1) nor any reasonable way to implement it ("A
> reference is a pointer in disguise") that allows for type conversion.

Why not? It's really simple: make an exception to the rule! :-)

No, seriously: that's really what happens here! A reference to
const may be bound to a temporary, thus allowing type conversion.

After all, have you ever thought how this could work:

void print_it (const int & x)
{
cout << x;
}

int main()
{
print_it (1);
// ...
}


HTH,

Carlos
--

KIM Seungbeom

unread,
Dec 27, 2002, 3:40:44 AM12/27/02
to
Carlos Moreno wrote:
>
> Matt Bitten wrote:
> > Effective C++ suggests overloading operators so as to mimic the
> > built-in
> > operators. Based on this, operator+ should not be a member function, so
> > that type conversion can be performed on the first operand, and it
> > should
> > be prototyped as
> >
> > const Foo operator+(const Foo &, const Foo &);
> >
> > I have two questions about this.
> >
> > (1) I hear that some people say one should NEVER return const by value.
>
> Who said that? They're undoubtedly wrong. In a case like this,
> you *should* return by value, with const qualification -- not that
> catastrophe will happen if you don't; it's just that, as you said,
> you want to mimic the behaviour of built-ins, which would not
> return a modifiable rvalue.

Andrei Alexandrescu said that.
From http://www.cuj.com/experts/2102/alexandr.htm :
On a philosophical note, any return value is transitory
par excellence; it's an ephemerid that just was created
and will disappear soon. Then, why force operator+'s client
to get a constant value? What's constant about this butterfly?
Seen from this perspective, const temporary looks like
an oxymoron, a contradiction in terms. Seen from a practical
perspective, const temporaries force copying at destination.

He also mentioned that John Lakos recommends against returning const
values, in Large-Scale C++ Software Design, Section 9.1.9.

--
KIM Seungbeom <musi...@bawi.org>

Glenn G. Chappell

unread,
Dec 27, 2002, 4:56:45 AM12/27/02
to
Carlos Moreno <moreno_at_mo...@xx.xxx> wrote in message
news:<JuFO9.145$Zh3....@wagner.videotron.net>...

> Matt Bitten wrote:
> > (1) I hear that some people say one should NEVER return const by value.
>
> Who said that? They're undoubtedly wrong. In a case like this,
> you *should* return by value, with const qualification -- not that
> catastrophe will happen if you don't; it's just that, as you said,
> you want to mimic the behaviour of built-ins, which would not
> return a modifiable rvalue.

Well, John Lakos said that (roughly). In section 9.1.9 (pp.618-619) of
"Large-Scale C++ Software Design" he writes

Guideline
Avoid declaring results returned by value from functions as const.

For user-defined types, his justification (p.619) is that a const object
"cannot be manipulated by non-const member functions." So, for example,
we could not do

(a+b).func();

if func() were not const.

My off-the-top-of-my-head thought on this issue is that, if you really
need to apply a non-const member function to the return value of
operator+, then you have a poorly designed class. But I'd be interested
in any counterexamples anyone might be able to dig up.

--
Glenn G. Chappell <>< * E-mail addr. in header is for
Dept. of Mathematical Sciences * moderators. To find my main
University of Alaska Fairbanks * addr., use a search engine.

Erik Max Francis

unread,
Dec 27, 2002, 4:57:54 AM12/27/02
to
Matt Bitten wrote:

> (1) I hear that some people say one should NEVER return const by
> value.
> Can anyone explain why const-by-value might be a problem? Avoiding it
> would clearly contradict the mimicking principle from EC++ mentioned
> above, since built-in types returned by value are (effectively) always
> const.

I'm not familiar with any common guidelines dictating that. It makes
good sense, sometimes, to return a const by value, although admittedly
those times are pretty rare. In the case of an overloaded operator +,
it has some marginal benefit (as you almst certainly know from the item
in _Effective C++_) because it prevents people from writing nonsensical
things like

(a + b) = c;

--
Erik Max Francis / m...@alcyone.com / http://www.alcyone.com/max/
__ San Jose, CA, USA / 37 20 N 121 53 W / &tSftDotIotE
/ \ All the gods are dead except the god of war.
\__/ Leroy Eldridge Cleaver
Bosskey.net: Aliens vs. Predator 2 / http://www.bosskey.net/avp2/
A personal guide to Aliens vs. Predator 2.

Andrei Alexandrescu

unread,
Dec 27, 2002, 4:59:24 AM12/27/02
to
"Matt Bitten" <mbit...@yahoo.com> wrote in message
news:f865ded1.02122...@posting.google.com...

> Effective C++ suggests overloading operators so as to mimic the built-in
> operators. Based on this, operator+ should not be a member function, so
> that type conversion can be performed on the first operand, and it
> should
> be prototyped as
>
> const Foo operator+(const Foo &, const Foo &);
>
> I have two questions about this.

You can find extensive answers to these questions at
http://www.moderncppdesign.com/publications/cuj-02-2003.html.


Andrei

Gabriel Dos Reis

unread,
Dec 27, 2002, 12:00:37 PM12/27/02
to
glenn_g_...@yahoo.com (Glenn G. Chappell) writes:

| My off-the-top-of-my-head thought on this issue is that, if you really
| need to apply a non-const member function to the return value of
| operator+, then you have a poorly designed class.

*Why*?

--
Gabriel Dos Reis, g...@integrable-solutions.net

Gabriel Dos Reis

unread,
Dec 27, 2002, 12:00:55 PM12/27/02
to
Erik Max Francis <m...@alcyone.com> writes:

[...]

| In the case of an overloaded operator +,
| it has some marginal benefit (as you almst certainly know from the
item
| in _Effective C++_) because it prevents people from writing
nonsensical
| things like
|
| (a + b) = c;

What is the last time you saw people writing that in real application?

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Thomas Mang

unread,
Dec 27, 2002, 12:01:43 PM12/27/02
to

Carlos Moreno schrieb:

> Matt Bitten wrote:
> > Effective C++ suggests overloading operators so as to mimic the
> > built-in
> > operators. Based on this, operator+ should not be a member function,
so
> > that type conversion can be performed on the first operand, and it
> > should
> > be prototyped as
> >
> > const Foo operator+(const Foo &, const Foo &);
> >
> > I have two questions about this.
> >
> > (1) I hear that some people say one should NEVER return const by
value.
>
> Who said that? They're undoubtedly wrong. In a case like this,
> you *should* return by value, with const qualification -- not that
> catastrophe will happen if you don't; it's just that, as you said,
> you want to mimic the behaviour of built-ins, which would not
> return a modifiable rvalue.

Huuhh??
This "undoubtedly" makes it sound finite (same as the "NEVER", but IMO,
both
opinions are sometimes right, sometimes not.

As you mentioned, the built-in operators would return const values. But
the
built-in types are not classes in the
C++ sense, so they have different semantic.
By returning a const object, you load some disadvantages on you; like
not
calling non-const member functions, problems with templates and so on.
Indeed, my opinion is by default a non-reference return-value should be
non-const, unless some other strong arguments suggest the contrary.
The argument that clients should not be able to write code like (a + b)
= c
is IMHO not strong enough.

But indeed, as the discussion shows, this is probably also a matter of
style.

regards,

Thomas

Gabriel Dos Reis

unread,
Dec 27, 2002, 4:06:57 PM12/27/02
to
Thomas Mang <a980...@unet.univie.ac.at> writes:

| As you mentioned, the built-in operators would return const values.

More precisely, the built-ins return *rvalues*. So is a non-reference
type return value.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Erik Max Francis

unread,
Dec 27, 2002, 8:20:33 PM12/27/02
to
Gabriel Dos Reis wrote:

> What is the last time you saw people writing that in real application?

I can't recall anyone doing it. Still, it would be an undesirably
result and the returning a const non-reference prevents it.

--
Erik Max Francis / m...@alcyone.com / http://www.alcyone.com/max/
__ San Jose, CA, USA / 37 20 N 121 53 W / &tSftDotIotE

/ \ It is dark in my favorite dream.
\__/ Aaliyah
Polly Wanna Cracka? / http://www.pollywannacracka.com/
The Internet resource for interracial relationships.

Carlos Moreno

unread,
Dec 27, 2002, 8:22:38 PM12/27/02
to

KIM Seungbeom wrote:


GOD! All of a sudden, the Universe stopped making sense!! I feel like
in one of those Twilight Zone episodes :-(

People sometimes are really passionate about emulating what operators
do for built-ins, such as arguing that operator= must return a reference
to non-const, just because built-ins do that (in spite of the fact that
there are almost no "sane" examples of code where you would legitimately
take advantage of built-ins doing that).

But here, all of a sudden we want to do differently than built-ins???

Built-ins return a non-modifiable rvalue -- why, oh why, would we
want any different for UDT's?

Template code?? That's an example in favor of what I am proposing;
template code that needs operator+ returning a modifiable rvalue
would fail when used with built-ins (that's precisely the argument
in favor of making operator= return non-const; because template
code that would work for built-ins wouldn't work for such class).

As for the comment above, from Andrei Alexandrescu... "const
temporary" looks like an oxymoron??? On the contrary: a non-const
temporary is what looks to me like an oxymoron -- a temporary may
be bound to a *const* reference only: it's a temporary; it's
not something that it's supposed to live to see its state
modified in a way that's needed later (there is no "later" with
an unnamed temporary; it's supposed to be about to be destroyed!).

Why would anyone want to allow the following code:

void f (T & x);

int main()
{
T a, b;

f (a + b);
}

That code doesn't make sense (and fails to compile) if a and b are
built-ins. Why would it work?

I wonder if this would even work:

T & x = a + b;

// use x now

Will x be referring to something that exists??? (I know that if
x is declared as const T &, then the answer is yes, because the
lifetime of the temporary is extended to match the lifetime of
x... But *because* x is declared as a reference to const)


I don't know you guys, but from here, that looks like a horrible
break of symmetry -- the two notions (the rule about binding a
temporary to a reference *to const only* and the notion of
a temporary returned by value being non-const) seem to me in
direct contradiction.

Ultimately, I think this is one of those situations where the
extra flexibility gained by not restricting the return value
is much less than the risk of introducing bugs due to errors
that the compiler could have flagged.

Carlos
--

Carlos Moreno

unread,
Dec 27, 2002, 8:23:52 PM12/27/02
to

Gabriel Dos Reis wrote:

> Erik Max Francis <m...@alcyone.com> writes:
>
> [...]
>
> | In the case of an overloaded operator +,
> | it has some marginal benefit (as you almst certainly know from the
> item
> | in _Effective C++_) because it prevents people from writing
> nonsensical
> | things like
> |
> | (a + b) = c;
>
> What is the last time you saw people writing that in real application?


Well, never -- *because the compiler wouldn't let you*... Or
ultimately -- even if the compiler lets you do it --, because it
would be later found to be a bug due to an error that we could
almost call it a "typo". The point is, if the return value is
const-qualified, then the compiler would flag these "typos"
sooner rather than later.

Carlos
--

Dave Harris

unread,
Dec 27, 2002, 8:25:56 PM12/27/02
to
glenn_g_...@yahoo.com (Glenn G. Chappell) wrote (abridged):

> My off-the-top-of-my-head thought on this issue is that, if you really
> need to apply a non-const member function to the return value of
> operator+, then you have a poorly designed class. But I'd be interested
> in any counterexamples anyone might be able to dig up.

I doubt it's ever essential, but sometimes it could be convenient. For
example:
Rectangle a, b;
Rectangle c = (a+b).inflate( 1 );

What is gained by forbidding this? Do you think it is error-prone?

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

Glenn G. Chappell

unread,
Dec 28, 2002, 6:31:16 AM12/28/02
to
Gabriel Dos Reis <g...@integrable-solutions.net> wrote in message news:<m34r8zb...@uniton.integrable-solutions.net>...

> glenn_g_...@yahoo.com (Glenn G. Chappell) writes:
>
> | My off-the-top-of-my-head thought on this issue is that, if you really
> | need to apply a non-const member function to the return value of
> | operator+, then you have a poorly designed class.
>
> *Why*?

I imagine we have a bit of a misundersanding here. I meant that one
typically does not want to apply a non-const member function to a
*temporary*. Something like

x = a + b;

x.func();

where func() is non-const, is certainly no problem. But an expression
involving

(a + b).func()

where, again, func() in non-const, suggests bad design. Usually the
point of a non-const member function is to modify the object. Making a
special effort to produce a modification that is promptly thrown out,
suggests that something is amiss.

Again, I'd be interested in any good reason someone might be able to
think of, for having a non-const member function applied to a
temporary.

Glenn G. Chappell <>< * Preferred e-mail is
Dept. of Mathematical Sciences * chappell G at member
University of Alaska Fairbanks * dot ams dot org.

llewelly

unread,
Dec 28, 2002, 9:29:29 AM12/28/02
to
Carlos Moreno <moreno_at_mo...@xx.xxx> writes:
[snip]

> > He also mentioned that John Lakos recommends against returning const
> > values, in Large-Scale C++ Software Design, Section 9.1.9.
>
>
> GOD! All of a sudden, the Universe stopped making sense!! I feel
like
> in one of those Twilight Zone episodes :-(

Do you feel this way every time you read this newsgroup? There are no
iron-clad rules in the nebulous realm of coding standards.

[snip]


> Why would anyone want to allow the following code:
>
> void f (T & x);
>
> int main()
> {
> T a, b;
>
> f (a + b);
> }

This doesn't compile if T is as follows:

struct T {};

T operator+(T const&,T const&);

You can't bind a temporary to a non-const reference, *even if the*
*temporary itself is non-const*. So returning non-const doesn't
make your example suddenly compile

> That code doesn't make sense (and fails to compile) if a and b are
> built-ins. Why would it work?
>
> I wonder if this would even work:
>
> T & x = a + b;

No.
[snip]

Gabriel Dos Reis

unread,
Dec 28, 2002, 9:30:06 AM12/28/02
to
glenn_g_...@yahoo.com (Glenn G. Chappell) writes:

| Gabriel Dos Reis <g...@integrable-solutions.net> wrote in message
news:<m34r8zb...@uniton.integrable-solutions.net>...
| > glenn_g_...@yahoo.com (Glenn G. Chappell) writes:
| >
| > | My off-the-top-of-my-head thought on this issue is that, if you
really
| > | need to apply a non-const member function to the return value of
| > | operator+, then you have a poorly designed class.
| >
| > *Why*?
|
| I imagine we have a bit of a misundersanding here. I meant that one
| typically does not want to apply a non-const member function to a
| *temporary*. Something like
|
| x = a + b;
| x.func();
|
| where func() is non-const, is certainly no problem. But an expression
| involving
|
| (a + b).func()
|
| where, again, func() in non-const, suggests bad design.

I understood that from your earlier posting. That is the reason I
asked why you think that suggests a bad design.

| Usually the
| point of a non-const member function is to modify the object.

I can agree with this.

| Making a
| special effort to produce a modification that is promptly thrown out,
| suggests that something is amiss.

Why?

If that object has no use after having served its purpose (application
of the non-const function), I see no reason why it should be kept
around.

| Again, I'd be interested in any good reason someone might be able to
| think of, for having a non-const member function applied to a
| temporary.

Didn't you ever hear of proxy classes?

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Gabriel Dos Reis

unread,
Dec 28, 2002, 12:56:57 PM12/28/02
to
Carlos Moreno <moreno_at_mo...@xx.xxx> writes:

| As for the comment above, from Andrei Alexandrescu... "const
| temporary" looks like an oxymoron??? On the contrary: a non-const
| temporary is what looks to me like an oxymoron -- a temporary may
| be bound to a *const* reference only:

True, but irrelevant to the discussion at heand.

| it's a temporary; it's
| not something that it's supposed to live to see its state
| modified in a way that's needed later (there is no "later" with
| an unnamed temporary; it's supposed to be about to be destroyed!).

True but, again, irrelevant. Check out proxy classes.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

John Potter

unread,
Dec 28, 2002, 4:15:09 PM12/28/02
to
On 27 Dec 2002 20:22:38 -0500, Carlos Moreno
<moreno_at_mo...@xx.xxx> wrote:

> Why would anyone want to allow the following code:

> void f (T & x);

> int main()
> {
> T a, b;
>
> f (a + b);
> }

> That code doesn't make sense (and fails to compile) if a and b are
> built-ins. Why would it work?

It also fails for user defined type regardless of constness of VALUE
return.

> I wonder if this would even work:

> T & x = a + b;

Compile error for builtin or udt regardless of constness of VALUE.

> Ultimately, I think this is one of those situations where the
> extra flexibility gained by not restricting the return value
> is much less than the risk of introducing bugs due to errors
> that the compiler could have flagged.

You seem to have missed everything. Builtins return modifiable
rvalues. You may modify a builtin rvalue via any non-const member
function. Why should UDT be different?

John

Carlos Moreno

unread,
Dec 28, 2002, 8:51:37 PM12/28/02
to

John Potter wrote:

> On 27 Dec 2002 20:22:38 -0500, Carlos Moreno
> <moreno_at_mo...@xx.xxx> wrote:
>
>
>>Why would anyone want to allow the following code:
>>
>
>>void f (T & x);
>>
>
>>int main()
>>{
>> T a, b;
>>
>> f (a + b);
>>}
>>
>
>>That code doesn't make sense (and fails to compile) if a and b are
>>built-ins. Why would it work?
>>
>
> It also fails for user defined type regardless of constness of VALUE
> return.


True. My mistake. I was trying to say something like the following:

string s1 = "a", s2 = "b";
char & c = (s1+s2)[0];


In this example, s1+s2 is a temporary. If we don't make it const
(which, according to g++ 2.96, we don't), then we're in big trouble,
since string::operator[] returns a reference to non-const, which
may be bound to c -- only that, the way I see it, the lifetime of
c will exceed the lifetime of the character we're referring to,
since the temporary is gone by the time we use c.

That wouldn't happen if string's operator+ returns a const object.


>>Ultimately, I think this is one of those situations where the
>>extra flexibility gained by not restricting the return value
>>is much less than the risk of introducing bugs due to errors
>>that the compiler could have flagged.
>
> You seem to have missed everything. Builtins return modifiable
> rvalues. You may modify a builtin rvalue via any non-const member
> function.


Member function? Built-ins? (*now* I'm missing something! :-))

Modifiable rvalues? I won't discuss it, even though I was convinced
otherwise... But I wonder: what's the point of returning a
*modifiable* rvalue for built-ins op+ ?? How do you plan to
modify them? (can you post one example where you *do* modify
the result of a+b, where a and b are ints?).

Assignment operators require an lvalue; operator++ requires an
lvalue. A function that receives by reference to non-const can't
be used, since a temporary can't be bound to the reference.

What else can be used to modify the "modifiable" rvalue returned
by built-ins operators?

Carlos
--


Why should UDT be different?

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Carlos Moreno

unread,
Dec 28, 2002, 8:51:56 PM12/28/02
to

Gabriel Dos Reis wrote:

> Carlos Moreno <moreno_at_mo...@xx.xxx> writes:
>
> | As for the comment above, from Andrei Alexandrescu... "const
> | temporary" looks like an oxymoron??? On the contrary: a non-const
> | temporary is what looks to me like an oxymoron -- a temporary may
> | be bound to a *const* reference only:
>
> True, but irrelevant to the discussion at heand.


I don't think it's irrelevant, since the rationale is -- the way
I see it -- the same in both cases.

A temporary can be bound to a reference *to const* only. What
does this tell you? That using a temporary for a function that
will (or could) change it, should be avoided, since it would
lead to bad surprises. You are modifying something that is
a temporary, so you're wasting time in applying the modifications.

Give me a reason why a temporary can't be bound to a reference
that does not apply to a const-reference.

Or let's put it this other way: it may be said that the difference
between calling a method and calling a function that receives the
object as parameter by reference (or by pointer, passing "this"
as parameter) is purely syntactical. A method may be seen as a
global function that receives an extra parameter which is the
object. (I'm not saying that you can freely interchange them;
it's an analogy, since both operations are "physically" equivalent)

So, with this analogy in mind, why is it that you can not call
a function that receives (and modifies) the parameter by reference
passing a temporary, but you can call a method that modifies the
object using that temporary??


> | it's a temporary; it's
> | not something that it's supposed to live to see its state
> | modified in a way that's needed later (there is no "later" with
> | an unnamed temporary; it's supposed to be about to be destroyed!).
>
> True but, again, irrelevant. Check out proxy classes.


Well, maybe proxy classes is an example of a good reason to make
an exception to the rule. If you have an excellent reason to
return a non-const object for a certain class or design pattern,
then by all means do so. But that's no reason to do it in
every other situation.

Carlos
--

Gabriel Dos Reis

unread,
Dec 29, 2002, 6:20:10 AM12/29/02
to
Carlos Moreno <moreno_at_mo...@xx.xxx> writes:

| A temporary can be bound to a reference *to const* only. What
| does this tell you?

Nothing, except that it is built into the language. and I don't
necessarily with that rule. But that is old debate, which has nothing
to do with the subject at hand.

| That using a temporary for a function that
| will (or could) change it, should be avoided, since it would
| lead to bad surprises.

No, it doesn't say that. That just means that, if I really want to
bind an rvalue to reference, the reference ought to be const. Nothing
more.

| You are modifying something that is
| a temporary, so you're wasting time in applying the modifications.

No. The only person in position to say whether I'm wasting my time in
applying a non-const function to a temporary is me, not you. As
said elsewhere, didn't you ever heard of prory classes?

| Give me a reason why a temporary can't be bound to a reference
| that does not apply to a const-reference.

What is the releveance to the disussion at hand?

| Or let's put it this other way: it may be said that the difference
| between calling a method and calling a function that receives the
| object as parameter by reference (or by pointer, passing "this"
| as parameter) is purely syntactical.

[...]

A method may be seen as a
| global function that receives an extra parameter which is the
| object. (I'm not saying that you can freely interchange them;
| it's an analogy, since both operations are "physically" equivalent)

Proof by analogy is fraud.

[...]

| Well, maybe proxy classes is an example of a good reason to make
| an exception to the rule.

Now you're changing a "oxumoron" to "exception to the rule". I'll
wait for the next change <g>

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Matt Bitten

unread,
Dec 29, 2002, 6:23:37 AM12/29/02
to
This discussion on const return values is enlightening. Thanks to all who
have participated. I'm feeling the urge to summarize what has been said,
along with some of my own reading, so I think I will.

Three principles seem to be applicable.

(1) Writing operators for user defined types so as to emulate the
built-ins is a Good Thing ("Principle of Least Surprise").

(2) Appropriate use of const/non-const gives greater freedom to clients,
for example by allowing them to pass const values to a function.

(3) Appropriate use of const/non-const allows the compiler to catch bugs
for us.

Applying these principles to parameter passing is easy. For example, op+
for ints is clearly defined as if it takes const references, so (1) says
use const. We probably want to be able to add const values, so (2) says
use const. An op+ should probably not modify its parameters. If we write
code that does, we've made a mistake, so (3) says use const.

Unfortunately, when dealing with return values the principles are in
conflict. We cannot do something like (a+b) = c with ints, so (1) says
return a const when writing an op+ for a user-defined type (although John
Potter appears to disagree). On the other hand, we give clients the most
freedom by returning a non-const value, so (2) says use non-const.

Principle (3) is the tricky one. If the client modifies a temporary, is
this almost certainly a bug? And should I try to do something about bugs
in the client's code?

It appears that Carlos Moreno and others would say "yes" and "why not?"
to these questions. Andrei Alexandrescu would apparently answer "no" to
the second. Author John Lakos would apparently answer "no" to both.
After reading the thread so far, I am tentatively inclined to agree with
Lakos. Consider Dave Harris's example.

Dave Harris (bran...@cix.co.uk) wrote:
> I doubt it's ever essential, but sometimes it could be convenient. For
> example:
> Rectangle a, b;
> Rectangle c = (a+b).inflate( 1 );

When is it reasonable to modify a temporary? When we get and use the
modified value before the temporary is thrown away.

> What is gained by forbidding this? Do you think it is error-prone?

As of right now it looks like "little or nothing" and "no". Does anyone
disagree? Glenn G. Chappell suggests that a class like Rectangle above
might be badly designed. Perhaps this is true. Maybe member function
inflate should be const and return a temporary value. This would not
alter the above code.

Carlos Moreno (moreno_at_mo...@xx.xxx) wrote:
> char & c = (s1+s2)[0];

I'm afraid this example doesn't convince me. I bind non-const references
to variables. Anyone who does otherwise is asking for trouble.

Gabriel Dos Reis (g...@integrable-solutions.net) wrote:
> Check out proxy classes.

I don't understand what the relevance of this is. A short example would
be appreciated.

Graeme Prentice

unread,
Dec 29, 2002, 12:16:09 PM12/29/02
to
On 28 Dec 2002 16:15:09 -0500, jpo...@falcon.lhup.edu (John Potter)
wrote:

[snip]

>You seem to have missed everything. Builtins return modifiable
>rvalues. You may modify a builtin rvalue via any non-const member
>function. Why should UDT be different?

I don't understand what you mean here - can you give an example of a
modifiable builtin rvalue and how you modify it?

Can anyone explain why the proxy class returned by bitset operator[] has
two non const member functions i.e. the proxy class (usually named
"reference") has two operator= member functions which are both non const
- why do they need to be non const? Is it for efficiency?

The proxy class (reference) has to be returned by value by bitset
operator[] (it cannot sensibly be returned by reference) - but why
can't
it be returned as a const value?

Josuttis says the proxy reference class is returned by operator[] for
non
const bitsets to ... <quote> "enable the use of the return value as a
modifiable value (lvalue)" <end quote>

Why does it need to be a "modifiable lvalue" - it's returned by value
so
its not an lvalue anyway, and as far as I can see, it never needs to be
modified so why can't the operator= member functions of the reference
class be const members. You can't take the address of an element of a
bitset like you can with vector - if you could, it would need to be an
lvalue.

Here's a version of a bitset class with const operator= in the proxy
class
- it works ok with the limited functionality it provides. The bits are
stored in an array of bool for simplicity and everything's public also
for
simplicity.


#include <iostream>

struct xbitset
{
struct bitref;
bool bits[8];
void set( size_t bn, bool b )
{ bits[bn] = b; }
bool test( size_t bn )
{ return bits[bn]; }

bitref const operator[]( size_t bn )
{ return bitref( *this, bn ); }

struct bitref // proxy class
{
xbitset& bitdata;
size_t bitnum;
bitref(xbitset& bs, size_t s):
bitnum(s), bitdata(bs) {}
bitref const& operator=(bool b) const
{
bitdata.set( bitnum, b );
return *this;
}
bitref const& operator=( bitref const& br) const
{
bitdata.set( bitnum, br.bitdata.test(br.bitnum) );
return *this;
}
operator bool() const
{ return bitdata.test(bitnum); }
};
};

int main()
{
xbitset x;
x[0] = x[1] = true;
x[2] = x[3] = x[1];
for (int k=0; k<8; ++k )
{
std::cout << x[k] << ' ';
}
}

Graeme

Gabriel Dos Reis

unread,
Dec 29, 2002, 2:54:18 PM12/29/02
to
mbit...@yahoo.com (Matt Bitten) writes:

[...]

| Carlos Moreno (moreno_at_mo...@xx.xxx) wrote:
| > char & c = (s1+s2)[0];
|
| I'm afraid this example doesn't convince me.

Same here.

[...]

| Gabriel Dos Reis (g...@integrable-solutions.net) wrote:
| > Check out proxy classes.
|
| I don't understand what the relevance of this is.

A object of proxy class type, in essence, mediates between different
entities; usually it is transparent to user. An example of sucha
class in the standard library is std::vector<bool>::reference.

Maybe a canonical example is a class that can make it possible to
have a variable (or object) behave "differently" in assignment
context. As the language is currently designed, if you have a
function (member or not, although operator[] is the canonical example)
that returns a reference, there is no way you can decide whether you
would like that reference be used only as the left hand side of an
assignment. However, you may accomplish that through a proxy class
like std::vector<bool>reference. By their nature (one wants them to
be transparent to user), it should be possible to apply non-const
member functions to such an object, see
std::vector<bool>::reference::flip().

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Dave Harris

unread,
Dec 29, 2002, 11:56:24 PM12/29/02
to
moreno_at_mo...@xx.xxx (Carlos Moreno) wrote (abridged):

> Or let's put it this other way: it may be said that the difference
> between calling a method and calling a function that receives the
> object as parameter by reference (or by pointer, passing "this"
> as parameter) is purely syntactical. A method may be seen as a
> global function that receives an extra parameter which is the
> object. (I'm not saying that you can freely interchange them;
> it's an analogy, since both operations are "physically" equivalent)

One difference is the implicit conversions. With:
anObject.func();

anObject will not be converted. With:
func( anObject );

anObject may be converted implicitly. It's really implicit conversions
which are the problem. You may not know you have a temporary at all.

Applying changes to a temporary is not intrinsically wrong, any more
than
ignoring the return result of a function is. In both cases some work is
done and then thrown away.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

KIM Seungbeom

unread,
Dec 29, 2002, 11:56:43 PM12/29/02
to
Carlos Moreno wrote:
>
> A temporary can be bound to a reference *to const* only. What
> does this tell you? That using a temporary for a function that
> will (or could) change it, should be avoided, since it would
> lead to bad surprises. You are modifying something that is
> a temporary, so you're wasting time in applying the modifications.
>
> Give me a reason why a temporary can't be bound to a reference
> that does not apply to a const-reference.
>
> Or let's put it this other way: it may be said that the difference
> between calling a method and calling a function that receives the
> object as parameter by reference (or by pointer, passing "this"
> as parameter) is purely syntactical. A method may be seen as a
> global function that receives an extra parameter which is the
> object. (I'm not saying that you can freely interchange them;
> it's an analogy, since both operations are "physically" equivalent)
>
> So, with this analogy in mind, why is it that you can not call
> a function that receives (and modifies) the parameter by reference
> passing a temporary, but you can call a method that modifies the
> object using that temporary??

Okay, I see your point. Isn't the language rule inconsistent in that

// invoking a non-const member function on an rvalue
rvalue.non_const_memfn()

is allowed while

// binding an rvalue to a reference to non-const
func_that_takes_non_const(rvalue)

is not? It is often suggested as an example for the latter that

void increment(int& i) { i++; }
double d = 4;
increment(d);

is meaningless because the side effect will be discarded right away,
but the chances of mistake is due to the implicit conversion, not
due to the rvalue-ness. So the restriction should apply only in the
cases of implicit conversions, IMHO, as Stroustrup says in D&E:

It is more important to allow a useful feature
than to prevent every misuse.

I'm still looking forward to some examples which show the usefulness.

--
KIM Seungbeom <musi...@bawi.org>

Andrei Alexandrescu

unread,
Dec 30, 2002, 6:24:40 AM12/30/02
to
"Glenn G. Chappell" <glenn_g_...@yahoo.com> wrote in message

> Again, I'd be interested in any good reason someone might be able to
> think of, for having a non-const member function applied to a
> temporary.

Path String drive, dir;
...
ChangeDir((drive + dir).EnsureBackslashTerminated());


Andrei

Gabriel Dos Reis

unread,
Dec 30, 2002, 6:25:25 AM12/30/02
to
Graeme Prentice <g...@paradise.net.nz> writes:

| Can anyone explain why the proxy class returned by bitset operator[] has
| two non const member functions i.e. the proxy class (usually named
| "reference") has two operator= member functions which are both non const
| - why do they need to be non const? Is it for efficiency?

A proxy class, by definition, is _suuposed_ to act transparently (yes,
I said "supposed"). That means it ought to be possible to call
non-cont member functions for objects of such types as if the member
functions were called for the objects the proxies are surrogate for.

| The proxy class (reference) has to be returned by value by bitset
| operator[] (it cannot sensibly be returned by reference) - but why
| can't
| it be returned as a const value?

Why should it return by const value?

Certainly, it ought to be possible to say

a_bitset[4343] = a_bitset[9];

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Andrei Alexandrescu

unread,
Dec 30, 2002, 6:25:48 AM12/30/02
to
"Carlos Moreno" <moreno_at_mo...@xx.xxx> wrote in message
news:3E0CECEA...@xx.xxx...

> > | (a + b) = c;
> >
> > What is the last time you saw people writing that in real application?
>
> Well, never -- *because the compiler wouldn't let you*... Or
> ultimately -- even if the compiler lets you do it --, because it
> would be later found to be a bug due to an error that we could
> almost call it a "typo". The point is, if the return value is
> const-qualified, then the compiler would flag these "typos"
> sooner rather than later.

True. The error is unlikely but possible. It is a disadvantage of Mojo that
it lets such things pass through. I would consider the gains worth the
inconvenience.

I believe Mojo can be modified to disallow such assignments. (Concretely,
fnresult<T> should make operator= private.) Now the question is, if the
extra complication is worth the benefit.

Andrei

Graeme Prentice

unread,
Dec 30, 2002, 6:35:14 AM12/30/02
to
On 29 Dec 2002 14:54:18 -0500, Gabriel Dos Reis
<g...@integrable-solutions.net> wrote:

[snip]

>A object of proxy class type, in essence, mediates between different
>entities; usually it is transparent to user. An example of sucha
>class in the standard library is std::vector<bool>::reference.
>
>Maybe a canonical example is a class that can make it possible to
>have a variable (or object) behave "differently" in assignment
>context. As the language is currently designed, if you have a
>function (member or not, although operator[] is the canonical example)
>that returns a reference, there is no way you can decide whether you
>would like that reference be used only as the left hand side of an
>assignment. However, you may accomplish that through a proxy class
>like std::vector<bool>reference. By their nature (one wants them to
>be transparent to user), it should be possible to apply non-const
>member functions to such an object, see
>std::vector<bool>::reference::flip().

You can define flip as a const function in the reference class

bitref const& flip() const
{
bitdata.set( bitnum, !bitdata.test(bitnum) );
return *this;
}

I made a mistake in my other post where I said that the & operator would
need an lvalue - I didn't think long enough - if it's overloaded it
doesn't need to be an lvalue.

Also I see from reading what Josutis says about vector<bool>::reference -
he says its not a true lvalue - so where I quoted Josuttis in my other
post about bitset class where he said "enable the use of the return type
as a modifiable lvalue" - he was meaning the return type is a sort of
pseudo/proxy lvalue.

I guess some proxy class objects do need to be non const but when I looked
at bitset::reference I couldn't see why it needed to be.

vector<>.end() returns an iterator by non const value - if that were made
const, you would avoid the possibility of end()-- working on some
implementations and not others - but what would you lose - you'd lose
the ability to do end()-- where it worked and you wouldn't be able to pass
it to a function with a non const ref parameter.

Graeme

John Potter

unread,
Dec 30, 2002, 6:38:06 AM12/30/02
to
On 28 Dec 2002 20:51:37 -0500, Carlos Moreno
<moreno_at_mo...@xx.xxx> wrote:

> John Potter wrote:

> > It also fails for user defined type regardless of constness of VALUE
> > return.

> True. My mistake. I was trying to say something like the following:

> string s1 = "a", s2 = "b";
> char & c = (s1+s2)[0];

> In this example, s1+s2 is a temporary. If we don't make it const
> (which, according to g++ 2.96, we don't), then we're in big trouble,
> since string::operator[] returns a reference to non-const, which
> may be bound to c -- only that, the way I see it, the lifetime of
> c will exceed the lifetime of the character we're referring to,
> since the temporary is gone by the time we use c.

Yep. The standard requires that behavior. Have you been reading
Machiavelli?

> That wouldn't happen if string's operator+ returns a const object.

But it doesn't.

> >>Ultimately, I think this is one of those situations where the
> >>extra flexibility gained by not restricting the return value
> >>is much less than the risk of introducing bugs due to errors
> >>that the compiler could have flagged.

> > You seem to have missed everything. Builtins return modifiable
> > rvalues. You may modify a builtin rvalue via any non-const member
> > function.

> Member function? Built-ins? (*now* I'm missing something! :-))

Cool. You are still smiling. Built-ins are deprived of members.

> Modifiable rvalues? I won't discuss it, even though I was convinced
> otherwise... But I wonder: what's the point of returning a
> *modifiable* rvalue for built-ins op+ ?? How do you plan to
> modify them? (can you post one example where you *do* modify
> the result of a+b, where a and b are ints?).

Yep, built-ins are broken. Please submit a DR. They return a
modifiable rvalue which can't be modified. Operator= should be
a member and accept an rvalue like real objects.

> Assignment operators require an lvalue;

What is an assignment operator? Operator= works with rvalue.

> What else can be used to modify the "modifiable" rvalue returned
> by built-ins operators?

T mr ();
T const cr ();
T& ml ();
T const& cl ();

This is very nice. If T is a built-in, the first two are the same.
Obviously a problem with built-ins. Never use the second form and
there will be no problem. It is a shame that the built-ins make it
impossible to use const values. Don't try it for them or your types!

John

John Potter

unread,
Dec 30, 2002, 6:53:30 AM12/30/02
to
On 29 Dec 2002 12:16:09 -0500, Graeme Prentice <g...@paradise.net.nz>
wrote:

> On 28 Dec 2002 16:15:09 -0500, jpo...@falcon.lhup.edu (John Potter)
> wrote:

> >You seem to have missed everything. Builtins return modifiable
> >rvalues. You may modify a builtin rvalue via any non-const member
> >function. Why should UDT be different?

> I don't understand what you mean here - can you give an example of a
> modifiable builtin rvalue and how you modify it?

That's the catch. There are modifiable (non-const) and non-modifiable
(const) rvalues in C++. The built-ins only support the modifiable form
and provide no way to make the modification. They are second class
citizens with no members.

John

Andrei Alexandrescu

unread,
Dec 30, 2002, 11:45:45 AM12/30/02
to
"Carlos Moreno" <moreno_at_mo...@xx.xxx> wrote in message
news:3E0E1F0F...@xx.xxx...

> Well, maybe proxy classes is an example of a good reason to make
> an exception to the rule. If you have an excellent reason to
> return a non-const object for a certain class or design pattern,
> then by all means do so. But that's no reason to do it in
> every other situation.

How about exception safety and efficiency - would they matter as
important?

Andrei

Andrei Alexandrescu

unread,
Dec 30, 2002, 11:46:03 AM12/30/02
to
"Carlos Moreno" <moreno_at_mo...@xx.xxx> wrote in message
news:3E0CF18B...@xx.xxx...

> People sometimes are really passionate about emulating what operators
> do for built-ins, such as arguing that operator= must return a
reference
> to non-const, just because built-ins do that (in spite of the fact
that
> there are almost no "sane" examples of code where you would
legitimately
> take advantage of built-ins doing that).
>
> But here, all of a sudden we want to do differently than built-ins???

For efficiency reasons.

> Built-ins return a non-modifiable rvalue -- why, oh why, would we
> want any different for UDT's?

One reason is that built-ins don't have member functions. Member
functions
that mutate state /can/ be applied to temporary values, which make that
Universe you mention not quite coherent in the first place.

One other reason is that copying and moving are identical notions for
built-ins, and are different notions for UDTs.

All in all, it's nice to mimic int, but only, as Scott Meyers put it,
"when
in doubt". When there are other reasons (such as exception safety and
efficiency) at stake, I can put up with that much asymmetry.

> As for the comment above, from Andrei Alexandrescu... "const
> temporary" looks like an oxymoron??? On the contrary: a non-const
> temporary is what looks to me like an oxymoron -- a temporary may
> be bound to a *const* reference only: it's a temporary; it's
> not something that it's supposed to live to see its state
> modified in a way that's needed later (there is no "later" with
> an unnamed temporary; it's supposed to be about to be destroyed!).

The idea is that const is historically derived from ROM and memory paged
as
unmodifiable. Obviously, temporary values have no place in either place.

> I don't know you guys, but from here, that looks like a horrible
> break of symmetry -- the two notions (the rule about binding a
> temporary to a reference *to const only* and the notion of
> a temporary returned by value being non-const) seem to me in
> direct contradiction.

vector<string> v;
...
// Clear the vector
vector<string>().swap(v);
// v.swap(vector<string>()); wouldn't work

There was not much symmetry in the first place. I'm not sure if this is
good
news or bad news :o).

> Ultimately, I think this is one of those situations where the
> extra flexibility gained by not restricting the return value
> is much less than the risk of introducing bugs due to errors
> that the compiler could have flagged.

The risks are minor imho. After all, how many string-related errors
there
are due to operator+ returning non-const temporaries?


Andrei

Gabriel Dos Reis

unread,
Dec 30, 2002, 11:46:21 AM12/30/02
to
KIM Seungbeom <musi...@bawi.org> writes:

| I'm still looking forward to some examples which show the usefulness.

I mentioned proxy classes; an example in the standard library is given
by std::vector<bool>::reference.
I believe that in D&E (I don't have a copy at hand where I'm writing
from), Bjarn Stroustrup discusses how one can use proxy clases to
"simulate lvalue/rvalue" context.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Gabriel Dos Reis

unread,
Dec 30, 2002, 12:55:59 PM12/30/02
to
Graeme Prentice <g...@paradise.net.nz> writes:

| On 29 Dec 2002 14:54:18 -0500, Gabriel Dos Reis
| <g...@integrable-solutions.net> wrote:
|
| [snip]
|
| >A object of proxy class type, in essence, mediates between different
| >entities; usually it is transparent to user. An example of sucha
| >class in the standard library is std::vector<bool>::reference.
| >
| >Maybe a canonical example is a class that can make it possible to
| >have a variable (or object) behave "differently" in assignment
| >context. As the language is currently designed, if you have a
| >function (member or not, although operator[] is the canonical
example)
| >that returns a reference, there is no way you can decide whether you
| >would like that reference be used only as the left hand side of an
| >assignment. However, you may accomplish that through a proxy class
| >like std::vector<bool>reference. By their nature (one wants them to
| >be transparent to user), it should be possible to apply non-const
| >member functions to such an object, see
| >std::vector<bool>::reference::flip().
|
| You can define flip as a const function in the reference class
|
| bitref const& flip() const
| {
| bitdata.set( bitnum, !bitdata.test(bitnum) );

Is bitdata a data member of bitset? If so, then bitdata.set() is now
calling a (non-const) modifying function (I guess by that its name,
it will modifies bitdata) with an expression that gets const-qualified.
You lose.

[...]

| I guess some proxy class objects do need to be non const but when I
looked
| at bitset::reference I couldn't see why it needed to be.

Look again :-)

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Gabriel Dos Reis

unread,
Dec 30, 2002, 3:14:47 PM12/30/02
to
Graeme Prentice <g...@paradise.net.nz> writes:

| You can define flip as a const function in the reference class
|
| bitref const& flip() const

Now, flip() may be called for a const object to modify it -- which
defeats the whole purpose of -not- having const in the first place!

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Private Pyle

unread,
Dec 30, 2002, 6:06:37 PM12/30/02
to
When in doubt, go to the source. Let's see what Stroustrup has to
say.

The main reason not to declare the return value of an operator a const
is because you cannot in general be certain how that return value will
be used in a full expression. If the temporary created from an
operator defines a function with side effects, then the temporary
object needs to be mutable. For example, for a, b, c, and d of class
Foo, you might have:

c = ((a+b).doSomething()) + d;

If the result of (a+b) is a const foo&, and doSomething() is not a
const function, the compiler will complain:

§10.2.6: A const member function can be invoked for both const
and non-const objects, whereas a non-const member
function can be invoked only for non-const objects.

For an elaboration on this, see the section on temporaries (§10.4.10).


For your second question, we need to work backwards. First:

§11.5.2: An operation modifying the state of a class object
should therefore be a member or a global function
taking a non-const reference argument (or a non-const
pointer argument). Operators that require lvalue
operands for the fundamental types (=, *=, ++, etc.)
are most naturally defined as members for user-defined
types.

Conversely, if implicit type conversion is desired for
all operands of an operation, the function
implementing it must be a nonmember function taking a
const reference argument or a non-reference argument.
This is often the case for functions implementing
operators that do not require lvalue operands when
applied to fundamental types (+, -, ||, etc.). Such
operators often need access to the representations of
their operand class. Consequently, binary operators
are the most common source of friend functions.

Thus, if you desire implicit type conversion for your arguments, then
they must be either "T" or "const T&". Go back to find out why:

§11.3.4: [T]here is little difference between a function that
[takes a T argument] and one that [takes a const T&
argument]. This issue is discussed further in §11.6.

Forward references like this abound in Stroustrup, and aggravate me to
no end. Keep going back:

§10.4.10: Unless bound to a reference or used to initialize a
named object, a temporary object is destroyed at the
end of the full expression in which it was created.

A temporary can be used as an initializer for a const
reference or named object. Remember that [...] a
temporary object cannot be bound to a non-const
reference (§5.5).

So we see that initializing a named object and a const reference are
semantically equivalent. Understanding this is a little tricky. The
relationship between regular declarations and function arguments, and
the difference between a "regular" T& and a const T&, is established
even earlier:

§5.5: The semantics of argument passing are defined to be
those of initialization[.]

That is, the actual parameters to a function are initialized using the
values passed to them, using the same sematics as normal
initialization (construction).

Finally, to understand why conversion is applied to named objects and
const references, but not to references:

§5.5: Initialization of a reference is trivial when the
initializer is an lvalue (an object whose address you
can take; see §4.9.6). The initializer for a 'plain'
T& must be an lvalue of type T.

The initializer for a const T& need not be an lvalue
or even of type T. In such cases,
[1] first implicit type conversion to T is applied if
necessary (see §C.6);
[2] then, the resulting value is placed in a
temporary variable of type T; and
[3] finally, this temporary variable is used as the
value of the initializer.

A temporary created to hold a reference initializer
persists until the end of its reference's scope.

References to variables and references to constants
are distinguished because the introduction of a
temporary in the case of the variable is highly
error-prone; an assignment to the variable would
become an assignment to the - soon to disappear -
temporary. No such problem exists for references to
constants, and references to constants are often
important as function arguments (§11.6).

So there it is. A "T&" is a "plain" reference, while a "const T&" is
a reference to a constant. The reason that conversions are applied
only to const reference arguments of a function turns out to be purely
to prevent programmer stupidity. Conversions are applied to object
parameters because it is (apparently) obvious that making changes to
the object is making changes to a local copy. Conversions are NOT
applied to a non-const reference parameter because it is (apparently)
NOT obvious that, if a conversion is applied, the reference parameter
would point to a temporary.

§7.2: The absence of const in the declaration of a reference
argument is taken as a statement of intent to modify
the variable[.]

On 26 Dec 2002 00:02:24 -0500, mbit...@yahoo.com (Matt Bitten)
wrote:

>Effective C++ suggests overloading operators so as to mimic the
built-in
>operators. Based on this, operator+ should not be a member function, so
>that type conversion can be performed on the first operand, and it
>should
>be prototyped as
>
>const Foo operator+(const Foo &, const Foo &);
>
>I have two questions about this.
>
>(1) I hear that some people say one should NEVER return const by value.
>Can anyone explain why const-by-value might be a problem? Avoiding it
>would clearly contradict the mimicking principle from EC++ mentioned
>above, since built-in types returned by value are (effectively) always
>const.
>
>(2) I never thought about this before, but how can passing a const
>reference allow for type conversion? I cannot think of any reasonable
>intuitive meaning for a const reference ("A reference _is_ the object"
-
>from Cline's FAQ, 8.1) nor any reasonable way to implement it ("A
>reference is a pointer in disguise") that allows for type conversion.
>
>-- Matt

Thomas Mang

unread,
Dec 30, 2002, 6:07:52 PM12/30/02
to
> People sometimes are really passionate about emulating what operators
> do for built-ins, such as arguing that operator= must return a
reference
> to non-const, just because built-ins do that (in spite of the fact
that
> there are almost no "sane" examples of code where you would
legitimately
> take advantage of built-ins doing that).

Never got a problem when using STL-algorithms on vectors which simple
plain
pointers as iterators?
I do not consider this sane.

On the contrary, I haven't found out what this restriction for the
built-in types
is supposed to be good for.
There is so much in C++ you can get into nebulous territory; but this
protection
mechanism isn't one I believe to be effective.


regards,

Thomas

Graeme Prentice

unread,
Dec 30, 2002, 6:09:22 PM12/30/02
to
On 30 Dec 2002 06:25:25 -0500, Gabriel Dos Reis
<g...@integrable-solutions.net> wrote:

>Graeme Prentice <g...@paradise.net.nz> writes:
>
>| Can anyone explain why the proxy class returned by bitset operator[]
has
>| two non const member functions i.e. the proxy class (usually named
>| "reference") has two operator= member functions which are both non
const
>| - why do they need to be non const? Is it for efficiency?
>
>A proxy class, by definition, is _suuposed_ to act transparently (yes,
>I said "supposed"). That means it ought to be possible to call
>non-cont member functions for objects of such types as if the member
>functions were called for the objects the proxies are surrogate for.
>
>| The proxy class (reference) has to be returned by value by bitset
>| operator[] (it cannot sensibly be returned by reference) - but why
>| can't
>| it be returned as a const value?
>
>Why should it return by const value?
>
>Certainly, it ought to be possible to say
>
> a_bitset[4343] = a_bitset[9];

If you look at the code I posted with all const member functions in the
proxy class, it is possible to assign bits as you are doing


xbitset x;
x[0] = x[1] = true;

I didn't say the proxy class *should* return a const value - I just
pointed out that it is possible for it to do so with no loss of
functionality. I went looking for a proxy class in the std library that
needed to return a non const value and became curious as to why bitset
returns a non const proxy object. Maybe I'm missing something. It may
be
more efficient for it to be non const - I didn't investigate that.

To John : your initial comment about modifiable builtin rvalues was too
subtle for me - thanks for clearing it up.

Graeme

Graeme Prentice

unread,
Dec 30, 2002, 6:10:35 PM12/30/02
to
On 30 Dec 2002 12:55:59 -0500, Gabriel Dos Reis
<g...@integrable-solutions.net> wrote:

>Graeme Prentice <g...@paradise.net.nz> writes:
>
>|
>| You can define flip as a const function in the reference class
>|
>| bitref const& flip() const
>| {
>| bitdata.set( bitnum, !bitdata.test(bitnum) );
>
>Is bitdata a data member of bitset? If so, then bitdata.set() is now
>calling a (non-const) modifying function (I guess by that its name,
>it will modifies bitdata) with an expression that gets const-qualified.
>You lose.
>

I don't understand what you mean - the flip function I am defining here
is
a const member of the inner proxy class - there is nothing to stop it
passing bitnum by value to a non const member of the containing class.

bitdata is a member of the containing class, not the proxy class. ??

I haven't thought about const bitset objects though.

Graeme

Gabriel Dos Reis

unread,
Dec 31, 2002, 5:59:16 AM12/31/02
to
Graeme Prentice <g...@paradise.net.nz> writes:

| On 30 Dec 2002 12:55:59 -0500, Gabriel Dos Reis
| <g...@integrable-solutions.net> wrote:
|
| >Graeme Prentice <g...@paradise.net.nz> writes:
| >
| >|
| >| You can define flip as a const function in the reference class
| >|
| >| bitref const& flip() const
| >| {
| >| bitdata.set( bitnum, !bitdata.test(bitnum) );
| >
| >Is bitdata a data member of bitset? If so, then bitdata.set() is now
| >calling a (non-const) modifying function (I guess by that its name,
| >it will modifies bitdata) with an expression that gets const-qualified.
| >You lose.
| >
|
| I don't understand what you mean - the flip function I am defining here
| is
| a const member of the inner proxy class - there is nothing to stop it
| passing bitnum by value to a non const member of the containing class.
|
| bitdata is a member of the containing class, not the proxy class. ??

Really? How? Work out your example.

Given the above, either bitdata is a data member of the proxy class
(which I assumed in my previous message) or ??? I.e., what is the
declaration for bitdata to make the above sensible?

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Gabriel Dos Reis

unread,
Dec 31, 2002, 5:59:54 AM12/31/02
to
Graeme Prentice <g...@paradise.net.nz> writes:

| On 30 Dec 2002 12:55:59 -0500, Gabriel Dos Reis
| <g...@integrable-solutions.net> wrote:
|
| >Graeme Prentice <g...@paradise.net.nz> writes:
| >
| >|
| >| You can define flip as a const function in the reference class
| >|
| >| bitref const& flip() const
| >| {
| >| bitdata.set( bitnum, !bitdata.test(bitnum) );
| >
| >Is bitdata a data member of bitset? If so, then bitdata.set() is now
| >calling a (non-const) modifying function (I guess by that its name,
| >it will modifies bitdata) with an expression that gets const-qualified.
| >You lose.
| >
|
| I don't understand what you mean - the flip function I am defining here
| is
| a const member of the inner proxy class

And std::vector<bool>::operator[] returns an object of that type. If
the std::vector<bool> object is const, then your version of flip()
would attempt to modify it, since the class interface actively supports
that operation: you have now turned a compile-time error into an
undefined-behaviour. Is that what "const" on return value supposed to
buy us?

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

John Potter

unread,
Dec 31, 2002, 6:01:56 AM12/31/02
to
On 30 Dec 2002 18:09:22 -0500, Graeme Prentice <g...@paradise.net.nz>
wrote:

> If you look at the code I posted with all const member functions in the


> proxy class, it is possible to assign bits as you are doing
> xbitset x;
> x[0] = x[1] = true;
>
> I didn't say the proxy class *should* return a const value - I just
> pointed out that it is possible for it to do so with no loss of
> functionality.

I think you intended to talk about a const proxy object being returned
by operator[], not some const value returned by a proxy object.

Maybe you are missing the logical const wanted in the physical const
supplied by the language. For example, there is no need for a non-const
operator[] in vector. A const member may modify any element of the
implementation array because the data member within the const member is
a T* const not a T const*. It makes logical const sense to have both
operators to make a const vector look like it contains const elements.

The proxy class acts like a & not a const&. It does seem a bit strange
to have an assignment operator which transfers assignment to the
referent which is a const member. Trying to make make sense in a
language which only provides law.

John

Hillel Y. Sims

unread,
Dec 31, 2002, 6:11:27 AM12/31/02
to
"John Potter" <jpo...@falcon.lhup.edu> wrote in message
news:3e0fb921...@news.earthlink.net...

> That's the catch. There are modifiable (non-const) and non-modifiable
> (const) rvalues in C++. The built-ins only support the modifiable form
> and provide no way to make the modification. They are second class
> citizens with no members.
>

What kinds of member functions do you think built-ins should have to bring
more harmony to the language? How would they affect our coding styles?

thanks,
hys

--
(c) 2002 Hillel Y. Sims
hsims AT factset.com

Gabriel Dos Reis

unread,
Dec 31, 2002, 6:11:51 AM12/31/02
to
Graeme Prentice <g...@paradise.net.nz> writes:

| If you look at the code I posted with all const member functions in the
| proxy class, it is possible to assign bits as you are doing

But your code, turning flip() into a const member function, now
allows calling a mutating function for a const object!

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Gabriel Dos Reis

unread,
Dec 31, 2002, 6:12:09 AM12/31/02
to
Graeme Prentice <g...@paradise.net.nz> writes:

[...]

| >| The proxy class (reference) has to be returned by value by bitset
| >| operator[] (it cannot sensibly be returned by reference) - but why
| >| can't
| >| it be returned as a const value?
| >
| >Why should it return by const value?
| >
| >Certainly, it ought to be possible to say
| >
| > a_bitset[4343] = a_bitset[9];
|
| If you look at the code I posted with all const member functions in the
| proxy class, it is possible to assign bits as you are doing
| xbitset x;
| x[0] = x[1] = true;

And which operator= do you think will be called? Hint: the copy and
assignment operator is -not- const-qualified.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Graeme Prentice

unread,
Dec 31, 2002, 6:48:01 PM12/31/02
to
On 30 Dec 2002 15:14:47 -0500, Gabriel Dos Reis
<g...@integrable-solutions.net> wrote:

>Graeme Prentice <g...@paradise.net.nz> writes:
>
>| You can define flip as a const function in the reference class
>|
>| bitref const& flip() const
>
>Now, flip() may be called for a const object to modify it -- which
>defeats the whole purpose of -not- having const in the first place!

No it can't - according to Josuttis, the bitset class has this member
function

bool operator[]( size_t bn ) const
{ return bits[bn]; }

which means bitref objects are never created by const bitset objects
because the non const member operator[] of bitset cannot be called for
a const bitset.

bitref const operator[]( size_t bn )
{ return bitref( *this, bn ); }


Graeme

Graeme Prentice

unread,
Dec 31, 2002, 6:50:22 PM12/31/02
to
On 31 Dec 2002 05:59:16 -0500, Gabriel Dos Reis
<g...@integrable-solutions.net> wrote:


Ah sorry, I mis-led you slightly - bitdata is a data member (a
reference) of the proxy class but it's a reference to the non const
bitset object so the const member function flip() of the proxy class
can happily use it. The code is below.


This may get time-warped - I made another post which hasn't turned up
yet that explained the bitset class has two operator[] member
functions - one const that returns bool (for const bitsets) and one
non const that returns the proxy object. Hence the proxy object never
gets created for const bitsets.

Happy new year. :)

Graeme


#include <iostream>

struct xbitset
{
struct bitref;
bool bits[8];
xbitset(){ memset( bits, 0, 8 ); }
void set( size_t bn, bool b )
{ bits[bn] = b; }
bool test( size_t bn )
{ return bits[bn]; }

bitref const operator[]( size_t bn )
{ return bitref( *this, bn ); }

bool operator[]( size_t bn ) const
{ return bits[bn]; }

struct bitref
{
xbitset& bitdata;
size_t bitnum;
bitref(xbitset& bs, size_t s):
bitnum(s), bitdata(bs) {}
bitref const& operator=(bool b) const
{
bitdata.set( bitnum, b );
return *this;
}
bitref const& operator=( bitref const& br) const
{
bitdata.set( bitnum, br.bitdata.test(br.bitnum) );
return *this;
}
operator bool() const
{ return bitdata.test(bitnum); }

bitref const& flip() const
{
bitdata.set( bitnum, !bitdata.test(bitnum) );

return *this;
}
};
};

int main()
{


xbitset x;
x[0] = x[1] = true;

x[2] = x[3] = x[1];
for (int k=0; k<8; ++k )
{
std::cout << x[k] << ' ';
x[k].flip();
}
std::cout << std::endl;
for (int k=0; k<8; ++k )
{
std::cout << x[k] << ' ';
x[k].flip();
}
std::cout << std::endl;
for (int k=0; k<8; ++k )
{
std::cout << x[k] << ' ';
}
xbitset const y;
// y[0] = y[1] = true; // illegal
// y[0].flip(); // illegal
x[0] = y[0];

Graeme Prentice

unread,
Dec 31, 2002, 6:50:52 PM12/31/02
to
On 31 Dec 2002 06:12:09 -0500, Gabriel Dos Reis
<g...@integrable-solutions.net> wrote:

>Graeme Prentice <g...@paradise.net.nz> writes:
>
>[...]
>
>| >| The proxy class (reference) has to be returned by value by bitset
>| >| operator[] (it cannot sensibly be returned by reference) - but
why
>| >| can't
>| >| it be returned as a const value?
>| >
>| >Why should it return by const value?
>| >
>| >Certainly, it ought to be possible to say
>| >
>| > a_bitset[4343] = a_bitset[9];
>|
>| If you look at the code I posted with all const member functions in
the
>| proxy class, it is possible to assign bits as you are doing
>| xbitset x;
>| x[0] = x[1] = true;
>
>And which operator= do you think will be called? Hint: the copy and
>assignment operator is -not- const-qualified.

Hopefully the code in another post I just made will explain what
happens for const xbitset objects. If not, post the code that you
think produces undefined behaviour or allows modification of const
objects. For
xbitset const y;
y[0] has type bool

Copy construction can occur for the proxy object if you do this - but
that's not a problem either - or no more than it is for the std
library bitset class.

xbitset h;
bool p;

template <typename T>
void copy_proxy( T v )
{
h[0] = v;
p = v;
}

int main()
{
xbitset x;
copy_proxy( x[0] );
}


Let me repeat - I'm not saying that the proxy object returned by
operator[] for non const bitsets *should* be const (though I think its
more logical) - just that when I tried to find out why it needed to
be non const I couldn't see any reason.

Graeme

James Kanze

unread,
Dec 31, 2002, 6:55:34 PM12/31/02
to
Gabriel Dos Reis <g...@integrable-solutions.net> wrote in message
news:<m3n0mn5...@uniton.integrable-solutions.net>...
> KIM Seungbeom <musi...@bawi.org> writes:

> | I'm still looking forward to some examples which show the
usefulness.

> I mentioned proxy classes; an example in the standard library is given
> by std::vector<bool>::reference. I believe that in D&E (I don't have
> a copy at hand where I'm writing from), Bjarn Stroustrup discusses how
> one can use proxy clases to "simulate lvalue/rvalue" context.

You don't need anything as complicated as proxy classes (not that they
are very complicated) to show up the basic inconsistency in the
standard. Why is: `ostringstream() << 0' legal and `ostringstream() <<
"0"' not?

There are cases where modifying a temporary is a horrible programming
error, and there are cases where it is the desired behavior. My
formatting class used a syntax like:

std::cout << GB_Format( "%d" ).with( i ) << std::endl ;

The function GB_Format::with was non-const, and modified the internal
state of the object.

The rule against binding a temporary to a non-const is a pragmatic
concession to what was a frequent error a long time ago.

The decision as to whether your operator+ returns a const object or not
is a question of style. It certainly prevents some errors. Whether
people would make these errors without the const, I don't know. It does
"sort of" mimic the behavior of built-in types. As such, it is a "good
thing", when it is appropriate. (One could argue that if the operator+
is involved, it is always appropriate. I'm not really sure about that.)
It's the sort of rule which sounds good (at least to me), but in
practice doesn't seem to be important enough that I bother with it. And
whatever else you might think of it, it is definitely the sort of rule
which is only appropriate to value type objects; there are very often
reasons why you would call a non-const function on a temporary with
significant behavior.

--
James Kanze mailto:jka...@caicheuvreux.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung

James Kanze

unread,
Dec 31, 2002, 6:55:57 PM12/31/02
to
"Andrei Alexandrescu" <andre...@hotmail.com> wrote in message
news:<aunan0$87ut8$2...@ID-14036.news.dfncis.de>...

> "Carlos Moreno" <moreno_at_mo...@xx.xxx> wrote in message
> news:3E0E1F0F...@xx.xxx...
> > Well, maybe proxy classes is an example of a good reason to make an
> > exception to the rule. If you have an excellent reason to return a
> > non-const object for a certain class or design pattern, then by all
> > means do so. But that's no reason to do it in every other
> > situation.

> How about exception safety and efficiency - would they matter as
> important?

You'll have to explain the exception safety bit to me. I don't see how
the const-ness of a return value enters into it.

I presume that the efficiency issue has to do with RVO (or something
similar). If I write:

BigNumber n( a + b ) ;

(where a and b are also BigNumber), the compiler can construct the
return value directly in n if it is non-const, but not if it is const?
(I'm not sure that the compiler can't do it in both cases, but if you
tell me that some compilers don't do it for a const return value, but do
for the non-const, it wouldn't surprise me either.)

--
James Kanze mailto:jka...@caicheuvreux.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

James Kanze

unread,
Dec 31, 2002, 6:58:21 PM12/31/02
to
jpo...@falcon.lhup.edu (John Potter) wrote in message
news:<3e113aab...@news.earthlink.net>...

> The proxy class acts like a & not a const&.

Right. And since it is acting like a reference, and references are
always const, the function that returns it should return it as a const
object. That's what you're trying to say, right?

> It does seem a bit strange to have an assignment operator which
> transfers assignment to the referent which is a const member.

Why. If I write int& i, the reference is quite const -- I can't modify
it within the rules of the language. It can still appear on the left
side of an assignment, and transfers assignment to the referent.

You know, I think you've convinced me that Scott was right.

--
James Kanze mailto:jka...@caicheuvreux.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

James Kanze

unread,
Dec 31, 2002, 6:59:23 PM12/31/02
to
"Andrei Alexandrescu" <andre...@hotmail.com> wrote in message
news:<aunbk8$8p5kk$2...@ID-14036.news.dfncis.de>...
> "Glenn G. Chappell" <glenn_g_...@yahoo.com> wrote in message
> > Again, I'd be interested in any good reason someone might be able
> > to think of, for having a non-const member function applied to a
> > temporary.

> Path String drive, dir;
> ...
> ChangeDir((drive + dir).EnsureBackslashTerminated());

Why so complicated:

Drive( "d:" ).format() ;

Presumably format is a non-const function. (It does change the state,
after all.)

Seriously, I'm bothered by:

void log( std::ostream& message ) ;
log( std::ostringstream() << 0 ) ; // Legal...
log( std::ostringstream() << "0" ) ; // Illegal...

There's a definite asymetry floating around here somewhere.

Off hand, I have a number of cases where I change the state (by calling
non-const functions) of temporary objects. Never had any problems with
it. I also have (or had) one or two cases where a class supported an
operator+. No problem there. I've never had a case where I changed the
state of a temporary object whose type supported operator+, so I have no
problem if operator+ returns a const. And the rules for all this are so
complicated and confusing that trying to mimic built-in types exactly
just isn't worth it -- even if I succeed, that doesn't mean that the
ambiguities (like the above) will disappear.

In sum: if you want operator+ to return a const value, it doesn't bother
me. If you want it to return a non-const, it doesn't bother me either.
And all I ask of proxy classes is that they be consistent -- if
operator[] returns a const proxy, whose operator= is non-const, THAT
would bother me.

James Kanze

unread,
Dec 31, 2002, 7:01:00 PM12/31/02
to
Gabriel Dos Reis <g...@integrable-solutions.net> wrote in message
news:<m3el7z4...@uniton.integrable-solutions.net>...
> Graeme Prentice <g...@paradise.net.nz> writes:

> | On 29 Dec 2002 14:54:18 -0500, Gabriel Dos Reis
> | <g...@integrable-solutions.net> wrote:

> | [snip]

> | >A object of proxy class type, in essence, mediates between
> | >different entities; usually it is transparent to user. An example
> | >of sucha class in the standard library is
> | >std::vector<bool>::reference.

> | >Maybe a canonical example is a class that can make it possible to
> | >have a variable (or object) behave "differently" in assignment
> | >context. As the language is currently designed, if you have a
> | >function (member or not, although operator[] is the canonical
> | >example) that returns a reference, there is no way you can decide
> | >whether you would like that reference be used only as the left
> | >hand side of an assignment. However, you may accomplish that
> | >through a proxy class like std::vector<bool>reference. By their
> | >nature (one wants them to be transparent to user), it should be
> | >possible to apply non-const member functions to such an object,
> | >see std::vector<bool>::reference::flip().

> | You can define flip as a const function in the reference class

> | bitref const& flip() const
> | {
> | bitdata.set( bitnum, !bitdata.test(bitnum) );

> Is bitdata a data member of bitset?

It's probably a private data member declared:

vector<bool>& bitdata ;

As John said, we're talking about a "reference" here, so if it isn't
const, we have a problem in our model. (Real references are always
immutable.) Logically, std::vector< bool >::reference emulates a
bool&. So the answer is simple: flip here should have exactly the same
semantics as in the following:

bool b ;
b.flip() ;

After all, the whole point of a proxy class is to be transparent, and
make things work exactly as if the class weren't there, right. So the
question is: should the function be const or not to better emulate the
above?

I guess that all we've really proved is that vector<bool> is a bad
example of good programming practice. Which probably won't surprise
everyone.

--
James Kanze mailto:jka...@caicheuvreux.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

John Potter

unread,
Dec 31, 2002, 7:05:33 PM12/31/02
to
On 31 Dec 2002 06:11:27 -0500, "Hillel Y. Sims" <use...@phatbasset.com>
wrote:

> "John Potter" <jpo...@falcon.lhup.edu> wrote in message
> news:3e0fb921...@news.earthlink.net...

> > That's the catch. There are modifiable (non-const) and
non-modifiable
> > (const) rvalues in C++. The built-ins only support the modifiable
form
> > and provide no way to make the modification. They are second class
> > citizens with no members.

> What kinds of member functions do you think built-ins should have to
bring
> more harmony to the language? How would they affect our coding styles?

The problem is built-in assignment via magic versus udt assignment via
an operator= member function. Udt assignment accepts an rvalue and
introduces sequence points. If built-in assingment worked the same way,
we would not have the long standing issue trying to determine whether
(a = b) += c should be well defined for int as it is for string. That
would still leave ++ ++ a which is undefined for pointer and defined
for other iterator. Prefix increment is usually a member but unlike
assignment, there is no requirement. ++ Iter() might be well defined.
Postfix increment is another where Iter() ++ ++ is valid if it is a
member. I this case I can find no use and violate everyone elses
style standards by making it a non-member. I guess we would need to
allow const built-in rvalues, but built-in operators would still produce
non-const rvalues. In the above, (int() = b) += c would be valid, but
we don't know about int(b) += c because only the copy assignment must be
made a member.

Note that all of the problems are caused by functions with side effects.
Get rid of them and lvalues to produce a nice clean language. ;-)

Nothing is worth changing, it is only amusing newsgroup nonsense. My
coding style does not include preventing the client from doing useful
things like (a = b) += c in the name of safety from stupid things like
(a = b) = c. The client decides whether a + b = c is useful, I can't.
ostream("log.file").flush() << "Error 42\n";

John

llewelly

unread,
Dec 31, 2002, 7:06:47 PM12/31/02
to
jpo...@falcon.lhup.edu (John Potter) writes:
[snip]

> It makes logical const sense to have both
> operators to make a const vector look like it contains const elements.
[snip]

Why?

Everywhere in the stl, constness of a container implies
constness of its objects, but I don't know why that decision was
made.

It's inconsistent with pointers - and it prevents me from having a
non-modifiable container containing modifiable objects. I've more
than once written functions that need to modify the elements of
a vector, but had no need to insert or push_back new elements.

Gabriel Dos Reis

unread,
Jan 1, 2003, 5:33:24 AM1/1/03
to
ka...@gabi-soft.de (James Kanze) writes:

| And all I ask of proxy classes is that they be consistent -- if
| operator[] returns a const proxy, whose operator= is non-const, THAT
| would bother me.

But the copy and assignment ooerator cannot be const-qualified.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Gabriel Dos Reis

unread,
Jan 1, 2003, 7:56:20 AM1/1/03
to
ka...@gabi-soft.de (James Kanze) writes:

| jpo...@falcon.lhup.edu (John Potter) wrote in message
| news:<3e113aab...@news.earthlink.net>...
|
| > The proxy class acts like a & not a const&.
|
| Right. And since it is acting like a reference, and references are
| always const,

This bit sounds wrong to me: References are not object.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Gabriel Dos Reis

unread,
Jan 1, 2003, 7:56:56 AM1/1/03
to
ka...@gabi-soft.de (James Kanze) writes:

| Gabriel Dos Reis <g...@integrable-solutions.net> wrote in message
| news:<m3n0mn5...@uniton.integrable-solutions.net>...
| > KIM Seungbeom <musi...@bawi.org> writes:
|
| > | I'm still looking forward to some examples which show the
| usefulness.
|
| > I mentioned proxy classes; an example in the standard library is
given
| > by std::vector<bool>::reference. I believe that in D&E (I don't
have
| > a copy at hand where I'm writing from), Bjarn Stroustrup discusses
how
| > one can use proxy clases to "simulate lvalue/rvalue" context.
|
| You don't need anything as complicated as proxy classes (not that they
| are very complicated) to show up the basic inconsistency in the
| standard.

But, the purpose of my message wasn't to show up an inconsistency in the
Standard!

As indicated in the quote, I was answering a specific query which is:

I'm still looking forward to some examples which show the usefulness.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Allan W

unread,
Jan 1, 2003, 7:57:33 AM1/1/03
to
mbit...@yahoo.com (Matt Bitten) wrote
> Effective C++ suggests overloading operators so as to mimic the
built-in
> operators. Based on this, operator+ should not be a member function,
so
> that type conversion can be performed on the first operand, and it
> should
> be prototyped as
>
> const Foo operator+(const Foo &, const Foo &);
>
> I have two questions about this.
>
> (1) I hear that some people say one should NEVER return const by
value.

Yes, I've heard this too. My own rule is, never return by reference
(const or otherwise) from a non-assignment operator.

> Can anyone explain why const-by-value might be a problem?

If the return value wasn't const, you could enter code like this:
(a+b)=c;
which creates a temporary Foo, and then assigns a new value to the
temporary.

> Avoiding it
> would clearly contradict the mimicking principle from EC++ mentioned
> above, since built-in types returned by value are (effectively) always
> const.

I've never found anyone trying to write code like the above. I can,
however, imagine code like this:
(a+b).print(cout);
...Of course, this works with const too (assuming print is a const
function).

Bottom line: it's not the most important decision you'll ever have
to make.

> (2) I never thought about this before, but how can passing a const
> reference allow for type conversion? I cannot think of any reasonable
> intuitive meaning for a const reference ("A reference _is_ the object"
-
> from Cline's FAQ, 8.1) nor any reasonable way to implement it ("A
> reference is a pointer in disguise") that allows for type conversion.

void Bar1(const Foo&f) { std::cout << f.id(); }
void Bar2(Foo&f) { f.age++; }
struct Foo2 { // Note: NOT derived from Foo
operator Foo() { return Foo(); }
};

void baz() {
Foo matt;
Bar1(matt); // Ok, displays matt's ID
Bar2(matt); // OK, increments matt's age

Foo2 bitten;
Bar1(bitten); // Ok, creates a temporary foo() object
// and displays it's ID
Bar2(bitten); // ERROR! Can't pass a non-const Foo2 to Bar2!
}

If that last call DID work, here's what would happen:
* The compiler would create a temporary foo object and pass it to
Bar2
* Bar2 would modify the age of the TEMPORARY
* bitten would never have been modified
* The programmer would get angry, etc.

So, the compiler has a rule that it can't pass temporary arguments
by non-const reference. Const reference works fine, as does
non-const by value...

Gabriel Dos Reis

unread,
Jan 1, 2003, 7:57:51 AM1/1/03
to
ka...@gabi-soft.de (James Kanze) writes:

No: We're talking about proxy classes. Re-read this subthread.

[...]

| I guess that all we've really proved is that vector<bool> is a bad
| example of good programming practice.

No. That is a claim you made, for which no proof has been provided in
this thread.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Gabriel Dos Reis

unread,
Jan 1, 2003, 6:03:42 PM1/1/03
to
Gabriel Dos Reis <g...@integrable-solutions.net> writes:

| ka...@gabi-soft.de (James Kanze) writes:
|
| | And all I ask of proxy classes is that they be consistent -- if
| | operator[] returns a const proxy, whose operator= is non-const, THAT
| | would bother me.
|
| But the copy and assignment ooerator cannot be const-qualified.

Well, just after this message left my computer, I realized that I was
talking of another word.

Rob Stewart

unread,
Jan 1, 2003, 6:08:34 PM1/1/03
to
"llewelly" <llewe...@xmission.dot.com> wrote in message
news:864r8uu...@Zorthluthik.foo...

> jpo...@falcon.lhup.edu (John Potter) writes:
> [snip]
> > It makes logical const sense to have both
> > operators to make a const vector look like it contains const
elements.
>
> Everywhere in the stl, constness of a container implies
> constness of its objects, but I don't know why that
decision was
> made.

It clearly depends upon your idea of a const container. The
approach in the standard is that a const container looks the
same -- at least through its interface -- regardless of what
(const) operations you can invoke on it.

> It's inconsistent with pointers - and it prevents me from
having a
> non-modifiable container containing modifiable objects.
I've more
> than once written functions that need to modify the
elements of
> a vector, but had no need to insert or push_back new
elements.

The standard containers are unabashedly value based. They own the
values you put in them. In order to be unchanged (const), they
disallow changing the values they hold (unless you circumvent
that via a smart pointer with all const mfs giving non-const
access to the referent).

--
Rob

To reply, change nothing to bigfoot in the address shown.

Dave Harris

unread,
Jan 1, 2003, 6:10:14 PM1/1/03
to
llewe...@xmission.dot.com (llewelly) wrote (abridged):

> Everywhere in the stl, constness of a container implies
> constness of its objects, but I don't know why that decision was
> made.

Presumably because they mimic arrays. Given:
const int array[10];

if the array is const each element of it is also const.

More generally, if we have a "part of" relationship, const for the
container should imply const for the elements. For example if a Dog is
const its Leg should also be const.

If we have a "uses" or some other relationship, it can make sense for
the
constness not to transfer. For example, a const Dog might have a
non-const
Owner because they are separate objects.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

Rob Stewart

unread,
Jan 1, 2003, 6:11:41 PM1/1/03
to
"Glenn G. Chappell" <glenn_g_...@yahoo.com> wrote in message
news:1f5176b3.02122...@posting.google.com...

> Gabriel Dos Reis <g...@integrable-solutions.net> wrote in
message news:<m34r8zb...@uniton.integrable-solutions.net>...
> > glenn_g_...@yahoo.com (Glenn G. Chappell) writes:
> >
> > | My off-the-top-of-my-head thought on this issue is that,
if you really
> > | need to apply a non-const member function to the return
value of
> > | operator+, then you have a poorly designed class.
> >
> > *Why*?
>
> I imagine we have a bit of a misundersanding here. I meant that
one
> typically does not want to apply a non-const member function to
a
> *temporary*. Something like
>
> x = a + b;
> x.func();
>
> where func() is non-const, is certainly no problem. But an
expression
> involving
>
> (a + b).func()

If you do nothing else with x after the two statements above,
then these two fragments are identical in behavior. They only
look different. Thus, it is your sensibilities that are offended
by the latter fragment, not the proper functioning of the code.

> where, again, func() in non-const, suggests bad design. Usually
the
> point of a non-const member function is to modify the object.
Making a
> special effort to produce a modification that is promptly
thrown out,
> suggests that something is amiss.

That is your bias. If the UDT has value semantics, that is it
should look and act like a built-in numeric type, then returning
const is probably the Right Thing(tm) to do. If the UDT does not
have value semantics, then it probably represents some service or
system object like a log file. In that case, returning non const
is quite possibly the Right Thing(tm) to do. For that, refer to
examples cited by James Kanze and others in this thread.

Note that I said "probably" and "possibly" in the sentences
above. There are no iron-clad rules. I suppose returning const
might be a reasonable default style, but it shouldn't give you
much pause to return non-const. My wont is to return non-const by
default. I've never encountered a problem with it, though I
reserve the right to have such a problem in the future 8^}. Those
working in different domains than I may well have encountered
such problems already, so they may be more inclined to return
const by default. YMMV.

> Again, I'd be interested in any good reason someone might be
able to
> think of, for having a non-const member function applied to a
> temporary.

You've seen good examples by now. The above analysis should help
in choosing when to return const and eliminating unfounded biases
in your own code.

--
Rob

To reply, change nothing to bigfoot in the address shown.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

John Potter

unread,
Jan 1, 2003, 6:11:59 PM1/1/03
to
On 1 Jan 2003 07:57:51 -0500, Gabriel Dos Reis
<g...@integrable-solutions.net> wrote:

> ka...@gabi-soft.de (James Kanze) writes:

> | As John said, we're talking about a "reference" here,

> No: We're talking about proxy classes. Re-read this subthread.

Look again. We are talking about a particular proxy class which acts
as a reference. It has been demonstrated that all members could be
made const and there is no need for a non-const vector<bool>::reference
ever.

I don't understand your repeated statement that T::operator=(T const&)
(I assume that is what you mean by copy and assign) can't be const. In
the case of a proxy acting as a reference, the assignment is passed on
and does not change the proxy. Writing proxy1 = proxy2 produces
proxy1referent = proxy2referent and proxy1 == proxy2 will be true.

Note that iterator::operator*() is const because it does not modify the
iterator. All vector<bool>::reference members share this property and
could (should?) be const.

Elsewhere you have stated that a reference is not an object. So what?
A proxy object is trying to act like a reference and you want it to
act like an object also? Nonsense.

How about the other proxies in the standard? The istreambuf_iterator
uses one. It's operator* is shown as non-const. Obviously an error
which may be forgiven because it is not a required class. There is
never a need for a non-const istreambuf_iterator::proxy.

The output iterators (front_insert, back_insert, insert, ostream,
ostreambuf) are all self proxies. Copy assignment is not required
for output iterators. If it is not supported, there is never a need
for a non-const output iterator.

You started out to show a need for non-const value returns by using the
example of proxy classes. It seems that the result is that it has been
shown that proxy classes should never have non-const objects. Do you
have a counter example which *requires* a non-const proxy object?

I still think that returning const values from all functions is a silly
rule. I need no justification to not follow it.

John

John Potter

unread,
Jan 1, 2003, 6:12:17 PM1/1/03
to
On 31 Dec 2002 19:06:47 -0500, llewelly <llewe...@xmission.dot.com>
wrote:

> jpo...@falcon.lhup.edu (John Potter) writes:

> > It makes logical const sense to have both
> > operators to make a const vector look like it contains const
elements.

> Why?

> Everywhere in the stl, constness of a container implies
> constness of its objects, but I don't know why that decision was
> made.

Because it makes sense? Array is the classic container for which const
makes no sense and it applies to the elements.

> It's inconsistent with pointers - and it prevents me from having a
> non-modifiable container containing modifiable objects. I've more
> than once written functions that need to modify the elements of
> a vector, but had no need to insert or push_back new elements.

No problem, send the function the array that acts the way you want it
to act. Func(&v.front(), v.size()). Or, follow the stl and send it a
pair of iterators. Func(v.begin(), v.end()). Since you have no trust
in the function, never trust a function that expects a vector. Demand
an appropriate interface. You wrote the wrong functions.

John

John Potter

unread,
Jan 1, 2003, 6:12:52 PM1/1/03
to
On 31 Dec 2002 18:58:21 -0500, ka...@gabi-soft.de (James Kanze) wrote:

> jpo...@falcon.lhup.edu (John Potter) wrote in message
> news:<3e113aab...@news.earthlink.net>...
>
> > The proxy class acts like a & not a const&.
>
> Right. And since it is acting like a reference, and references are
> always const, the function that returns it should return it as a const
> object. That's what you're trying to say, right?

Not really, but since you read it that way and justified it, I will not
object. :)

> > It does seem a bit strange to have an assignment operator which
> > transfers assignment to the referent which is a const member.

> Why. If I write int& i, the reference is quite const -- I can't
modify
> it within the rules of the language. It can still appear on the left
> side of an assignment, and transfers assignment to the referent.

Yes, it does make sense now.

> You know, I think you've convinced me that Scott was right.

Too quick of a generalization. The conclusion is that
vector<bool>::reference objects should always be const. So,
vector<bool>::operator[] should return a const reference. Not to
be confused with const_reference which is bool. It does not follow
that all values returned by all functions should be const.

John

Graeme Prentice

unread,
Jan 1, 2003, 6:17:38 PM1/1/03
to
On 1 Jan 2003 07:56:20 -0500, Gabriel Dos Reis
<g...@integrable-solutions.net> wrote:

>ka...@gabi-soft.de (James Kanze) writes:
>
>| jpo...@falcon.lhup.edu (John Potter) wrote in message
>| news:<3e113aab...@news.earthlink.net>...
>|
>| > The proxy class acts like a & not a const&.
>|
>| Right. And since it is acting like a reference, and references are
>| always const,
>
>This bit sounds wrong to me: References are not object.

But you said (regarding proxy objects) "one wants them to be
transparent to the user". This is the behaviour of a reference. The
user isn't supposed to know the proxy object is even there. In the
case of bitset, the proxy object cannot be changed after it's been
created and always behaves like a reference to non const. It is
logically const and it seems to me the only consideration for whether
it is physically const or not is one of efficiency. I investigated
the bitset proxy class was to see if it was an example of a proxy
object that *has* to be non const and can see no reason - other than
the C++ standard specifies operator[] of bitset returns by non const
value.

Graeme

Gabriel Dos Reis

unread,
Jan 2, 2003, 12:17:37 AM1/2/03
to
jpo...@falcon.lhup.edu (John Potter) writes:

| I don't understand your repeated statement that T::operator=(T const&)
| (I assume that is what you mean by copy and assign) can't be const.

I posted a message saying that I was misleadiing on that particular
topic.
See Message-ID: <m3of71r...@uniton.integrable-solutions.net>

[...]

| Elsewhere you have stated that a reference is not an object. So what?

So a conclusion based on the assertion that a reference should be
"const" is ill-stated.

| A proxy object is trying to act like a reference and you want it to
| act like an object also? Nonsense.

No, that is not nonsense since the "proxy object" is an object.

[...]

| You started out to show a need for non-const value returns by using
the
| example of proxy classes. It seems that the result is that it has
been
| shown that proxy classes should never have non-const objects.

Would you mind quoting the proof of the fact that "proxy classes
should never have non-const object"? I didn't see it.

| Do you
| have a counter example which *requires* a non-const proxy object?

'const' is *never* _required_. One can do without it: Just by
avoiding it in the same way one can avoid classes. I bet the issue
is: Is it useful to have non-const member function for proxy classes?

But, I will nevertheless give a tentative answer to your question:
Imagine a proxy class object that does some bookkeeping in debugging
mode (e.g. that record some information each time it is used with a
mutating fonction for the underlying referenced object). Such a proxy
class needs a way to change the state the information it keeps.
Yes, you can use mutable + const to circumvent the non-const member
function. But, then, that is obfuscation.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Gabriel Dos Reis

unread,
Jan 2, 2003, 12:17:55 AM1/2/03
to
jpo...@falcon.lhup.edu (John Potter) writes:

| > You know, I think you've convinced me that Scott was right.
|
| Too quick of a generalization. The conclusion is that
| vector<bool>::reference objects should always be const. So,
| vector<bool>::operator[] should return a const reference.

Would that be really that usefull?

stf::vector<bool>::reference ref = v[8];

| Not to
| be confused with const_reference which is bool. It does not follow
| that all values returned by all functions should be const.

Yes.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

KIM Seungbeom

unread,
Jan 2, 2003, 7:44:15 AM1/2/03
to
Dave Harris wrote:
>
> llewe...@xmission.dot.com (llewelly) wrote (abridged):
> > Everywhere in the stl, constness of a container implies
> > constness of its objects, but I don't know why that decision was
> > made.
>
> Presumably because they mimic arrays. Given:
> const int array[10];
>
> if the array is const each element of it is also const.

I'm afraid your logic doesn't hold, because what you said is
an array of const int(corresponding to vector<const int>),
not a const array of int(const vector<int>).

>
> More generally, if we have a "part of" relationship, const for the
> container should imply const for the elements. For example if a Dog is
> const its Leg should also be const.
>
> If we have a "uses" or some other relationship, it can make sense for the
> constness not to transfer. For example, a const Dog might have a non-const
> Owner because they are separate objects.

True.

--
KIM Seungbeom <musi...@bawi.org>

Gabriel Dos Reis

unread,
Jan 2, 2003, 10:19:08 AM1/2/03
to
Graeme Prentice <g...@paradise.net.nz> writes:

| On 1 Jan 2003 07:56:20 -0500, Gabriel Dos Reis
| <g...@integrable-solutions.net> wrote:
|
| >ka...@gabi-soft.de (James Kanze) writes:
| >
| >| jpo...@falcon.lhup.edu (John Potter) wrote in message
| >| news:<3e113aab...@news.earthlink.net>...
| >|
| >| > The proxy class acts like a & not a const&.
| >|
| >| Right. And since it is acting like a reference, and references are
| >| always const,
| >
| >This bit sounds wrong to me: References are not object.
|
| But you said (regarding proxy objects) "one wants them to be
| transparent to the user".

Yes. I avoided, on purpose, the word "reference", because the proxy
object is an object and that will show up in one way or the other. I
even took many precautions when I said "one wants them to be
transparent to the user". Because, the user can always detect that
a proxy class to T is -not- a "T&".

One wants them to be transparent, but that does not mean they are
effectively made transparent to user. There are many ways in which
they are not actually transparent to users, as a "T&" would be.

| This is the behaviour of a reference. The
| user isn't supposed to know the proxy object is even there.

Ahem, I won't restart the debate why std::vector<bool> is not a
container. It suffices to mention that because "&a_bitset[0]" does not
work, the user is forced to know that the proxy class is there.

| In the
| case of bitset, the proxy object cannot be changed after it's been
| created and always behaves like a reference to non const.

No, it doesn't. You can't say

std::vector<bool> v(299);

&v[10];

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Graeme Prentice

unread,
Jan 2, 2003, 10:20:02 AM1/2/03
to
On 2 Jan 2003 00:17:37 -0500, Gabriel Dos Reis
<g...@integrable-solutions.net> wrote:

>
>'const' is *never* _required_. One can do without it: Just by
>avoiding it in the same way one can avoid classes. I bet the issue
>is: Is it useful to have non-const member function for proxy classes?
>
>But, I will nevertheless give a tentative answer to your question:
>Imagine a proxy class object that does some bookkeeping in debugging
>mode (e.g. that record some information each time it is used with a
>mutating fonction for the underlying referenced object). Such a proxy
>class needs a way to change the state the information it keeps.
>Yes, you can use mutable + const to circumvent the non-const member
>function. But, then, that is obfuscation.


In the process of writing this post, I finally found a difference in
functional operation depending on const or non const return value of
operator[] of bitset but you have to read to the end to find out what
it is :)

There is a slight flaw in the std::bitset class and probably
vector<bool> as demonstrated by the following code. When I call
fx1(x1[0]); I'm passing by value and not intending that my bitset
gets modified - but it does!!! Maybe this is already a known
problem.

What is a proxy class - the definition is a bit vague. Stroustrup
says "a handle that provides an interface that is close to identical
to the class for which it is a handle is often called a proxy".

If you take proxy to mean "temporary transparent object" then it
should die at the end of the full expression in which it is created,
in which case adding state data to it after it is created, contradicts
the definition of "proxy class". If you extend the definition of
proxy class then non const proxy objects are probably useful.


Why is the reference class in bitset and vector<bool> public? I can't
think of any reason at all. Without a public copy constructor, the
code below wouldn't compile but the class doesn't need to be public
for the implicitly defined copy constructor to be accessible. This
template function might be an unlikely scenario and due to the fact
that it allows unintended modification of a bitset object I think it
would be better if the copy constructor and reference class were
private.

Why does the reference class have a user defined destructor - is this
binding on the library vendor or just to indicate the destructor is
public? The default constructor is explicitly shown as private so its
not possible for users to create their own reference object other than
with a template function. The public destructor is still accessible
if the reference class is private.

Making the reference object const doesn't avoid the template function
problem because the constness doesn't make it into the template
function parameter type and even if it did, wouldn't prevent the
unintended modification of the bitset.

Are the library class definitions shown in the standard binding on the
library implementation? Why doesn't the bitset class have a
bool operator[](size_t s)const;
for const bitsets?

Josuttis describes the reference class as "internal" to vector<bool>
so declarations like this
std::vector<bool>::reference const & v = bb[0];
x1[0] = v;
or this
std::bitset<8>::reference const & v = x1[0];
x1[0] = v;

would seem to have not been intentionally available, even though
potentially useful as wrapping a "bit pointer" - however, the const
reference can only read the value (due to operator= being a non const
member of the reference class), so what's the point of using a
reference& - it might as well be a bool. Devious users can use a
template function to turn the rvalue proxy object into an lvalue proxy
object and bind to a non const reference and allow use as a writeable
bit pointer but this is way too devious to have been intentionally
legal.

Guess what - after all this long discussion I've finally found a
difference between a non const and const return value for operator[]
of bitset !!!!! - if the operator= member functions are const, then
the const&v just above can be used to write to the bitset object.
Allowing users to create references to proxy objects is outside the
spirit of a transparent proxy class though.

I could easily be wrong about some of this.

A tab is a tab. A proxy object is a proxy object. A reference to a
proxy object is two references. :)

// std::bitset problem
// you need trigraphs enabled to compile this code

#include <iostream>
#include <bitset>

bool b1,b2;

template <typename T>
void fx1( T v )
{
b1 = v;
v = true;
b2 = v;
}

int main()
{
std::bitset<8> x1; // all zero
fx1( x1[0] ); // passing by value is safe??/
why has my bitset now been changed ??!
std::cout << b1 << ' '
<< b2 << ' '
<< x1[0] << std::endl;
}


Graeme

Graeme Prentice

unread,
Jan 2, 2003, 8:29:05 PM1/2/03
to
On 2 Jan 2003 10:19:08 -0500, Gabriel Dos Reis
<g...@integrable-solutions.net> wrote:

>Graeme Prentice <g...@paradise.net.nz> writes:
>
>
>| In the
>| case of bitset, the proxy object cannot be changed after it's been
>| created and always behaves like a reference to non const.
>
>No, it doesn't. You can't say
>
> std::vector<bool> v(299);
>
> &v[10];

Yeah I knew that - I just meant that it's behaviour is a sort of
subset of reference to non const. Once the bitset proxy object is
created the selection of which bit it refers to can't be changed
unless member functions are added to the bitset or reference class.
>From the user's point of view (s)he's doing a one shot read or
write/read of a boolean value.

Graeme

Dave Harris

unread,
Jan 2, 2003, 8:34:26 PM1/2/03
to
musi...@bawi.org (KIM Seungbeom) wrote (abridged):

> I'm afraid your logic doesn't hold, because what you said is
> an array of const int(corresponding to vector<const int>),
> not a const array of int(const vector<int>).

Why do you think
const int array[10];

corresponds to:
vector<const int> vec_c;

rather than:
const vector<int> c_vec;

? With vec_c I can push_back more items. With array I can't.

It seems to me that with both vectors and arrays, the elements are part-of
the vector, rather than independent objects. For example, they will be
destroyed when the vector/array is destroyed.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Graeme Prentice

unread,
Jan 3, 2003, 12:56:43 AM1/3/03
to

Hopefully this message will turn up at the same time as the message I
just posted.

Not long after I hit send on my last message, I realised why in the
case of vector<bool>, the copy constructor is public - it has a
static member function swap that needs it.

static void swap(reference x, reference y);

Still doesn't need the reference class to be public though. If these
parameters were reference to const then the operator= functions would
need to be const. If the parameters are reference to non const then
they can't bind to the temporary proxy objects.

If bitset operator[] returns a const object then std::swap compiles
but works incorrectly - std::swap( b[0], b[1] )
depending on the initial settings, a value might get changed or it
might not. If the copy constructor was private, std::swap wouldn't
compile. Since the std specifies bitset operator[] returns a non
const object then std::swap won't compile but who would do that
anyway.

Graeme

KIM Seungbeom

unread,
Jan 3, 2003, 8:38:01 AM1/3/03
to
Dave Harris wrote:
>
> musi...@bawi.org (KIM Seungbeom) wrote (abridged):
> > I'm afraid your logic doesn't hold, because what you said is
> > an array of const int(corresponding to vector<const int>),
> > not a const array of int(const vector<int>).
>
> Why do you think
> const int array[10];
>
> corresponds to:
> vector<const int> vec_c;
>
> rather than:
> const vector<int> c_vec;
>
> ? With vec_c I can push_back more items. With array I can't.

You cannot push_back more items into an array regardless of the
constness. :-) Thus let's confine the topic to the modification
of existing elements.

My point was just that putting "const" in front of "int array[10];"
doesn't make the array itself const; it's because the elements
are const, not because the array itself is const, that you cannot
modify the elements.

C90 6.5.3 states: "If the specification of an array type includes
any type qualifiers, the element type is so-qualified, not the
array type." It has an example, too:

<example>
typedef int A[2][3];
const A a = {{4, 5, 6}, {7, 8, 9}}; /* array of array of const int */
</example>

>
> It seems to me that with both vectors and arrays, the elements are part-of
> the vector, rather than independent objects. For example, they will be
> destroyed when the vector/array is destroyed.

Agreed. That's why the constness of a vector implies the constness
of all the elements contained in the vector.

--
KIM Seungbeom <musi...@bawi.org>

John Potter

unread,
Jan 3, 2003, 3:40:23 PM1/3/03
to
On 2 Jan 2003 00:17:55 -0500, Gabriel Dos Reis
<g...@integrable-solutions.net> wrote:

> jpo...@falcon.lhup.edu (John Potter) writes:

> | Too quick of a generalization. The conclusion is that
> | vector<bool>::reference objects should always be const. So,
> | vector<bool>::operator[] should return a const reference.

> Would that be really that usefull?

> stf::vector<bool>::reference ref = v[8];

No problem.
typedef bool const_reference;
class Proxy { /* same as current reference */ };
typedef Proxy const reference;

If a const_reference can be a value, why not a reference a const value?

John

Andrei Alexandrescu

unread,
Jan 4, 2003, 2:15:14 AM1/4/03
to
"James Kanze" <ka...@gabi-soft.de> wrote in message
news:d6651fb6.02123...@posting.google.com...
> The rule against binding a temporary to a non-const is a pragmatic
> concession to what was a frequent error a long time ago.

IMHO, it also matters where the temporary comes from. The often-quoted
error involves a temporary coming from an implicit conversion. However,
when the temporary was explicitly created, my opinion is that the
language
could safely allow it to bind to a non-const reference.

Andrei

Gabriel Dos Reis

unread,
Jan 4, 2003, 2:17:52 AM1/4/03
to
jpo...@falcon.lhup.edu (John Potter) writes:

| On 2 Jan 2003 00:17:55 -0500, Gabriel Dos Reis
| <g...@integrable-solutions.net> wrote:
|
| > jpo...@falcon.lhup.edu (John Potter) writes:
|
| > | Too quick of a generalization. The conclusion is that
| > | vector<bool>::reference objects should always be const. So,
| > | vector<bool>::operator[] should return a const reference.
|
| > Would that be really that usefull?
|
| > stf::vector<bool>::reference ref = v[8];
|
| No problem.
| typedef bool const_reference;
| class Proxy { /* same as current reference */ };
| typedef Proxy const reference;
|
| If a const_reference can be a value, why not a reference a const
value?

My initial motivation in asking my question was: What problem would
the additional const solves? I believe that that const just drops out
implicitly most of the time, without notice.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

0 new messages