Topic: const references to references


Author: llewelly.at@xmission.dot.com (llewelly)
Date: Fri, 7 May 2004 21:06:34 +0000 (UTC)
Raw View
nobody@example.com (Ian McCulloch) writes:

> I don't know if it is correct form to reply to my own post like this (sorry
> - if that is the case), but I would add that the following program is
> well-formed (8.3.2: cv-qualified references are ill-formed except when the
> cv-qualifiers are introduced through the use of a typedef (7.1.3) or a
> template type argument (1.4.3), in which case the cv-qualifiers are
> ignored)
>
> template <typename T>
> void foo(T const x)
> {
>   x = 5;
> }
>
> int main()
> {
>   int y = 0;
>   foo<int&>(y);
> }
>
> But using the current issue 106, this would fail if the argument to foo were
> changed to T const&.
>
> To put it more succinctly, combining typedef's with cv qualifications would
> depend on the sequence in which they are combined; eg
>
> template <typename T>
> struct foo
> {
>    typedef T const  Tc;
>    typedef Tc&      Tcref1;
>    typedef T const& Tcref2;
> };
>
> I would expect foo<T>::Tcref1 and foo<T>::Tcref2 to always refer to the same
> type.  However, for a reference type, foo<X&>::Tcref1 would be X&, but
> foo<X&>::Tcref2 would be X const&.
[snip]

Without the special rules for references, foo<X&>::Tcref2 would be
    reference to const reference(0) to X. But C++ doesn't allow
    references to references. Should C++ ignore the first 'reference'
    applied, resulting in 'reference to const X', or should it ignore
    the second reference applied, resulting in 'const reference to
    X'? I don't know which is better, but const is ignored when it is
    applied to a reference, so the second example ignores a
    cv-qualifier. It seems *not* ignoring const errs on the side of
    safety.

(0) by 'const reference' I mean a reference which is const, *not* a
    reference which refers to a const object. Because this is the
    opposite of what standard uses 'const refernece' to mean, I
    originally tried to write this post using the the phrase
    'reference which is const' in place of 'const refernece', but
    that ended up seeming much harder to read ... *sigh*.


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Ian McCulloch <nobody@example.com>
Date: Mon, 10 May 2004 01:17:16 CST
Raw View
llewelly wrote:

> nobody@example.com (Ian McCulloch) writes:
[snip]
>> To put it more succinctly, combining typedef's with cv qualifications
>> would depend on the sequence in which they are combined; eg
>>
>> template <typename T>
>> struct foo
>> {
>>    typedef T const  Tc;
>>    typedef Tc&      Tcref1;
>>    typedef T const& Tcref2;
>> };
>>
>> I would expect foo<T>::Tcref1 and foo<T>::Tcref2 to always refer to the
>> same
>> type.  However, for a reference type, foo<X&>::Tcref1 would be X&, but
>> foo<X&>::Tcref2 would be X const&.
> [snip]
>
> Without the special rules for references, foo<X&>::Tcref2 would be
>     reference to const reference(0) to X. But C++ doesn't allow
>     references to references. Should C++ ignore the first 'reference'
>     applied, resulting in 'reference to const X', or should it ignore
>     the second reference applied, resulting in 'const reference to
>     X'? I don't know which is better, but const is ignored when it is
>     applied to a reference, so the second example ignores a
>     cv-qualifier. It seems *not* ignoring const errs on the side of
>     safety.
>
> (0) by 'const reference' I mean a reference which is const, *not* a
>     reference which refers to a const object. Because this is the
>     opposite of what standard uses 'const refernece' to mean, I
>     originally tried to write this post using the the phrase
>     'reference which is const' in place of 'const refernece', but
>     that ended up seeming much harder to read ... *sigh*.

Yes, I think the standard language is a real hinderance to understanding
this problem.  The phrase "const pointer to T" versus "pointer to const T"
has an unambiguous meaning, of "T* const" and "T const*" respectively.  But
it is usual to talk about "const reference to T" to mean "T const&" which
has the opposite semantics!

I agree that not ignoring the extra cv-qualifiers errs on the side of
safety, but IMHO it is also wrong.

The reason why the phrase "const reference to T" is used at all is because,
unlike the pointer case, it has an unambigous meaning, precisely because
the possible alternate meaning (T& const) is not useful - references are
always const because there is no way in the language to modify (ie. reseat)
them.  The only sensible thing to do with T& const is to treat it as
equivalent to T&, which C++ already does (with the caveat that it is not
possible to form such a type except via a typedef or template).  The
proposed resolution violates this principle by shifting the cv
qualification from the reference to the referent, breaking what is surely
an invariant of the type system.

I guess the real question is, which resolution is more useful.  We can't
forget 'volatile' either, in some respects this is more important than
'const', because ignoring a 'const' would probably only have the effect of
allowing some code to compile where it otherwise would not, whereas
'volatile' affects the observable output of the program.  But I haven't
been able to come up with any sensible examples where T& volatile& would
appear at all - never mind considering whether treating it as T& versus T
volatile& makes more sense.

I have provided an example where ignoring the second-level const is the
'correct' thing to do - are there any examples where ignoring the const is
the 'wrong' thing to do?

Cheers,
Ian McCulloch


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: nobody@example.com (Ian McCulloch)
Date: Fri, 30 Apr 2004 16:34:45 +0000 (UTC)
Raw View
Hi,

I have been playing around with some experimental library that uses a proxy
class to encapsulate a reference.  The idea is that these have semantics as
close to C++ references as possible (but it uses reference counting under
the hood so there are no lifetime issues).  All of the interesting methods
of the proxy class are const, since the references are not reseatable.
This is also consistent with ordinary references; "int& const" is not a
valid type, but only because the 'const' is redundant.

Anyway, it would be pointless to require a non-const proxy references to be
able to perform mutating operations, since given a const proxy one could
just copy construct a non-const proxy that refers to the same data.

But looking at the result, I realized that a lot of it wouldn't have the
correct semantics (or even wouldn't compile) if the proxy reference was
replaced with a real reference and taking the current proposed resolution
to issue 106 for references to references,
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_defects.html#106

The reason is that it is quite possible to pass a proxy reference by
const-reference (which has the same effect as pass by value).  eg.

template <typename ProxyReference>
void foo(ProxyReference const& x)
{
   x = 5;
}

As I argued previously, it would be a pointless restriction to pass by
non-const reference here.  But, if 'ProxyReference' were of type T&, then
the proposed resolution to issue 106 would make the parameter of foo of
type T& const& -> T const& and it wouldn't work!

I cannot think of any rationale that says T& const& should be equivalent to
T const& (except that it is slightly unintuitive - which by itself is not a
good rationale), but maybe I am thinking about this the wrong way.  But as
well as the previous counter-argument, if one considers a reference to be
analagous to a const pointer with an implicit dereference, then the
proposed resolution is analagous to treating T* const as T const*.  Is
there a flaw in this argument?  What is the correct way of rationalizing
references to references?

Cheers,
Ian McCulloch


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: nobody@example.com (Ian McCulloch)
Date: Sun, 2 May 2004 19:43:25 +0000 (UTC)
Raw View
I don't know if it is correct form to reply to my own post like this (sorry
- if that is the case), but I would add that the following program is
well-formed (8.3.2: cv-qualified references are ill-formed except when the
cv-qualifiers are introduced through the use of a typedef (7.1.3) or a
template type argument (1.4.3), in which case the cv-qualifiers are
ignored)

template <typename T>
void foo(T const x)
{
  x = 5;
}

int main()
{
  int y = 0;
  foo<int&>(y);
}

But using the current issue 106, this would fail if the argument to foo were
changed to T const&.

To put it more succinctly, combining typedef's with cv qualifications would
depend on the sequence in which they are combined; eg

template <typename T>
struct foo
{
   typedef T const  Tc;
   typedef Tc&      Tcref1;
   typedef T const& Tcref2;
};

I would expect foo<T>::Tcref1 and foo<T>::Tcref2 to always refer to the same
type.  However, for a reference type, foo<X&>::Tcref1 would be X&, but
foo<X&>::Tcref2 would be X const&.

Cheers,
Ian McCulloch


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]