Discussion:
References to const
(too old to reply)
Robert
2015-07-29 22:30:22 UTC
Permalink
Hi, I am reading C++ Primer but am having some difficulty understanding
references to const. I am hoping that this turns out to be quite a
simple topic and I find that I am only thinking it is complex.

I understand that a reference is simply another name for an object and
that we can bind references to objects that are non const or const.
Further a reference to const is unique in that it cannot be used to
change the object to which the reference is bound.

My difficulty in understanding concerns the initialization rules for
references to const.

There is a general rule in c++ (subject to two exceptions) which says
that the type of a reference must match exactly the type of the object
to which it refers when it is initialized. Now according to my book, one
exception to this rule concerns a reference to const:

C++ Primer page 61 "..we can initialize a reference to const from any
expression that can be converted to the type of the reference. In
particular, we can bind a reference to const to a nonconst object, a
literal, or a more general expression."

What is the reason for this exception?

Well reading on, the book does not (in my opinion) explain this section
very well. I cannot understand what follows next and have quoted it
verbatim in case you don't have the book:

"The easiest way to understand the difference in initialization rules is
to consider what happens when we bind a reference to an object of a
different type:

double dval = 3.14;
const int &ref = dval;

Here ref refers to an int. Operations on ref will be integer operations,
but dval is a floating point number, not an integer. To ensure that the
object to which ref is bound is an int, the compiler transforms this
code into something like

const int temp = deval; // create a temporary const int from the double
const int &ref = temp; bind ref to that temporary

In this case, ref is bound to a temporary object. A temporary object is
an unnamed object created by the compiler when it needs a place to store
a result from evaluating an expression...

Now consider what could happen if this initialization were allowed but
ref was not a const. If ref weren't const, we could assign to ref. Doing
so would change the object to which ref is bound. That object is a
temporary, not dval. The programmer who made ref refer to dval would
probably expect that assigning to ref would change dval. After all, why
assign to ref unless the intent is to change the object to which ref is
bound? Because binding a reference to a temporary is almost surely not
what the programmer intended, the language makes it illegal."

----
I have read this several times today and seem to be getting more
confused each time I read it so will perhaps come back to it in the
morning. I would really appreciate it if someone who has a clear
understanding of this issue could explain this to me in a simple step by
step manner! Why can a reference to const be bound to an object that
does not match the reference type exactly? Why would anyone want to do
this in real code? Alternatively, could you recommend any other text
books or articles that might help me?


I think part of my problem is that I cannot see how this might apply to
real code - it all seems very abstract. However,I have a feeling that
this is a really *really* important basic topic that I need to grasp
before moving on.

Best wishes
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
legalize+ (Richard)
2015-07-30 02:41:48 UTC
Permalink
[Please do not mail me a copy of your followup]
Post by Robert
C++ Primer page 61 "..we can initialize a reference to const from any
expression that can be converted to the type of the reference. In
particular, we can bind a reference to const to a nonconst object, a
literal, or a more general expression."
What is the reason for this exception?
It means that you can have a function or method that accepts a parameter by
const reference and call that function or method with an instance of a
non-const object.

This is really the only way that it makes sense to use const references;
otherwise the only thing you could associate with a const reference would be
a const object and most objects change at *some* point in the program.

Consider the following function:

bool ends_with_foo(const std::string &text) {
return text.size() >= 3U && text.substr(text.size()-3) == "foo"; }

This function is saying that it's parameter 'text' is obtained by a const
reference to std::string. The const means we promise not to modify the
argument inside the function -- which is good because this function is
acting like a predicate and it shouldn't be changing the object under
inspection.

If we could only call 'ends_with_foo' on const strings, this would be
unfortunate, because we'd end up doing needless copies just to be able to
call this predicate. The rule that allows a const reference to be
initialized from a non-const object makes the predicate useful in the
obvious way:

std::string ender{"I end with foo"};
std::string unender{"I end with bar"};
bool yes = ends_with_foo(ender);
bool no = ends_with_foo(unender);

Otherwise I'd have to get things into a constant string just to be able to
query it:

std::string ender{"I end with foo"};
const std::string const_ender{ender};
std::string unender{"I end with bar"};
const std::string const_unender{unender}; bool yes =
ends_with_foo(const_ender); bool no = ends_with_foo(const_unender);

--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
legalize+ (Richard)
2015-07-31 13:12:24 UTC
Permalink
{ as the only moderator currently active I assure you no reformatting
of code in any post in the recent months has been undertaken. -mod }

[Please do not mail me a copy of your followup]
Post by legalize+ (Richard)
bool ends_with_foo(const std::string &text) {
return text.size() >= 3U && text.substr(text.size()-3) == "foo"; }
I don't know who is reformatting my code; I certainly didn't post it
in this form.

I don't mind moderation, but I see no reason for moderators to
reformat my code to *their* personal taste.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Alf P. Steinbach
2015-08-01 13:32:12 UTC
Permalink
* Richard Thomson
Post by legalize+ (Richard)
bool ends_with_foo(const std::string &text) {
return text.size() >= 3U && text.substr(text.size()-3) == "foo"; }
I don't know who is reformatting my code; I certainly didn't post it
in this form.
I don't mind moderation, but I see no reason for moderators to
reformat my code to *their* personal taste.
Re the moderation policy question:

This is a misunderstanding: the moderators (or moderator!, thanks Victor
Bazarov for holding the fort) don't reformat code other than, sometimes,
making it fit into reasonably short lines, e.g. 76 characters (*). But
there are many possible causes of reformatting when you post on Usenet.
I can /guess/ that the formatting change is that the right brace has
been moved, that it was originally on a third line.

For clc++m the moderation process involves resending your posting with
headers added, via an ordinary mail client, which can just Do Things.
There is a possibility that your posting was sent, by your client
software, with "flowed" format, and if a line ends with a space then in
"flowed" format that indicates that it's part of a paragraph, i.e. the
space is regarded as a line continuation. The moderator's editor can
then automatically join up the line, without the moderator noticing at all.

There are much worse things that can happen, in particular with line
lengths and encodings. When you look at the raw underlying text it's
often no longer very recognizable, as it was in the old days, e.g. due
to e-mail conventions like "quoted printable" sneaking their way into
Usenet (not to mention HTML and base 64). And the tools employed no
longer work easily at the raw text level, so, we just have to contend
with what our imperfectly-smart tools do for us, and hope that at the
human level we can still manage to communicate meaningfully. ;-)

Cheers & hth.,

- Alf, a currently not-active mod

Notes:
(*) The moderation guideline is, "If possible, fit your text in 70
columns". The guidelines are available at the URL noted below.
--
Using Thunderbird as Usenet client, Eternal September as NNTP server.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Martin Bonner
2015-07-30 17:01:56 UTC
Permalink
Post by Robert
There is a general rule in c++ (subject to two exceptions) which says
that the type of a reference must match exactly the type of the object
to which it refers when it is initialized. Now according to my book, one
C++ Primer page 61 "..we can initialize a reference to const from any
expression that can be converted to the type of the reference. In
particular, we can bind a reference to const to a nonconst object, a
literal, or a more general expression."
What is the reason for this exception?
Because it is very useful, and is safe.

It is safe because if I have a non-const object, and initialize a
reference-to-const with it, then the fact I can't modify the object via the
reference-to-const is not very interesting.

It is useful because if I have a function that doesn't need to modify an
object, I can declare the function parameter as reference-to-const - but I
can still pass a non-const object to the function.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jack Adrian Zappa
2015-09-04 13:53:48 UTC
Permalink
Post by Robert
...
C++ Primer page 61 "..we can initialize a reference to const from any
expression that can be converted to the type of the reference. In
particular, we can bind a reference to const to a nonconst object, a
literal, or a more general expression."
What is the reason for this exception?
The reason is that a reference is basically an alias to another
value/object.

double dval = 3.14;
double& rdval = dval;
rdval = rdval * 2; // dval has been changed to 6.28(*).

If you want a modify a value/object's converted representation, you
wouldn't use a reference but a converted value/object, like this:

double dval = 3.14;
int ival = dval;

Since ival is NOT a reference, a programmer wouldn't assume that
changing ival would change dval.

int& rival = dval; //< this is illegal

The reasons that is illegal is because dval would have to create an
unnamed temporary which is considered a rvalue and cannot be modified.
Therefore you cannot bind it to a non-const reference.

int const& const_rival = dval;

If const_rival happened to be an object, the life of the temporary
that is bound to it would be extended to the end of the enclosing
scope, otherwise, it would be free to be destroyed once it has been
used. However, this is getting beyond the scope of your original
question.

(*) Please note that a double is a floating point number and as such
you should never do a equal comparison due to rounding error of
equivilant representations. ALWAYS compare a floating point
against an error range.


JAZ
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Martin Bonner
2015-09-11 13:01:10 UTC
Permalink
Post by Jack Adrian Zappa
(*) Please note that a double is a floating point number and as such
you should never do a equal comparison due to rounding error of
equivalent representations. ALWAYS compare a floating point
against an error range.
I think that "ALWAYS" is too strong. In your example (involving 2 * 3.14)
it
is correct, but I believe the following code is reasonable:
double d = 2.0;
double d2 = d * d;
assert(d2 == 4.0);

(Note: The standard doesn't guarantee the assert won't fire, but it is
very, very, unlikely to do so.)
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Öö Tiib
2015-09-11 18:32:32 UTC
Permalink
Post by Martin Bonner
Post by Jack Adrian Zappa
(*) Please note that a double is a floating point number and as such
you should never do a equal comparison due to rounding error of
equivalent representations. ALWAYS compare a floating point
against an error range.
I think that "ALWAYS" is too strong. In your example (involving 2 * 3.14)
it
double d = 2.0;
double d2 = d * d;
assert(d2 == 4.0);
He didn't claim that floating point arithmetic always results
with slightly off results. It just happens frequently enough
to make 'operator==' unreliable in practice. People who want
to write reliable code must avoid unreliable constructs.

Your "reasonable" is also too strong. That code will be
optimized to nothing on most compilers so it feels difficult
to reason why to write it. ;)
Post by Martin Bonner
(Note: The standard doesn't guarantee the assert won't fire, but it is
very, very, unlikely to do so.)
AFAIK even 'assert(d2 == 3.0);' is not required to fire on
conforming C++ implementation. Floating point is quite totally
implementation-defined.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
James Kuyper
2015-09-12 19:57:32 UTC
Permalink
Post by Öö Tiib
Post by Martin Bonner
Post by Jack Adrian Zappa
(*) Please note that a double is a floating point number and as such
you should never do a equal comparison due to rounding error of
equivalent representations. ALWAYS compare a floating point
against an error range.
I think that "ALWAYS" is too strong. In your example (involving 2 * 3.14)
it
double d = 2.0;
double d2 = d * d;
assert(d2 == 4.0);
He didn't claim that floating point arithmetic always results
with slightly off results. It just happens frequently enough
to make 'operator==' unreliable in practice. People who want
to write reliable code must avoid unreliable constructs.
Your "reasonable" is also too strong. That code will be
optimized to nothing on most compilers so it feels difficult
to reason why to write it. ;)
Well, the assert was just a demonstration. In real-world code, the
assert() would be replaced by an if(), and might be widely separated
from the line which calculates d2.
The point is that real-world floating point implementations can produce
reliable results, so long as the final result and all intermediate steps
involve values that have the form of sufficiently small integers
multiplied by small positive or negative powers of two. It's not
entirely unreasonable to count on such things, even though the standard
doesn't provide any support for such assumptions.
Post by Öö Tiib
Post by Martin Bonner
(Note: The standard doesn't guarantee the assert won't fire, but it is
very, very, unlikely to do so.)
AFAIK even 'assert(d2 == 3.0);' is not required to fire on
conforming C++ implementation. Floating point is quite totally
implementation-defined.
assert(DBL_MAX - DBL_MIN > DBL_MIN - DBL_MAX) is not required to
trigger, either. I'd be interested in knowing if there's any real-world
implementation where it doesn't.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Continue reading on narkive:
Loading...