Topic: conversion from const char * to const char *&


Author: ntalsani@rochester.rr.com
Date: Tue, 30 Mar 2004 05:04:57 +0000 (UTC)
Raw View
I am trying to figure out which compiler is providing the proper behavior in
the following code which has an implicit cast from const char * to const
char *&

Here is the sample code:

#include <iostream>
#include <string>
void tester(const char *&parm)
{std::cout << parm << std::endl;}

int main()
{
    std::string foo = "test string";
    tester(foo.c_str());
}

Under one complier, this code compiles and runs just fine. Under a different
compiler, I get a compiler error stating something (I dont have access to
that compiler from here) about needing an lvalue to convert from const char
* to const char *&.

I dont have ready access to a copy of the standard to see what it says on
the matter, so I was hoping that someone could let me know which compiler
was correct (and why so that when I tell my coworker which compiler is right
I can explain it to him. )

Thanks for any help

Neil

---
[ 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: rogero@howzatt.demon.co.uk ("Roger Orr")
Date: Tue, 30 Mar 2004 16:10:12 +0000 (UTC)
Raw View
<ntalsani@rochester.rr.com> wrote in message
news:Bq4ac.93385$KB.22540@twister.nyroc.rr.com...
> I am trying to figure out which compiler is providing the proper behavior
in
> the following code which has an implicit cast from const char * to const
> char *&
>
> Here is the sample code:
>
> #include <iostream>
> #include <string>
> void tester(const char *&parm)
> {std::cout << parm << std::endl;}
>
> int main()
> {
>     std::string foo = "test string";
>     tester(foo.c_str());
> }

The standard does not let you pass a non-const reference to a temporary
object.

If the signature of tester is changed to:

void tester(const char * const &parm);

[That is, parm is a reference to a _constant_ pointer to const char]

then the code will compile cleanly.
(There are some compilers which are more tolerant than others about
compiling code
which passes non-const references to temopraries - some compile with a
warning and
others compile silently.)

HTH,
Roger Orr
--
MVP in C++ at www.brainbench.com


---
[ 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: technews@kangaroologic.com ("Jonathan Turkanis")
Date: Tue, 30 Mar 2004 17:24:56 +0000 (UTC)
Raw View
""Roger Orr"" <rogero@howzatt.demon.co.uk> wrote in message
news:c4b614$mq3$1$830fa795@news.demon.co.uk...
> <ntalsani@rochester.rr.com> wrote in message
> news:Bq4ac.93385$KB.22540@twister.nyroc.rr.com...
> > I am trying to figure out which compiler is providing the proper
behavior
> in
> > the following code which has an implicit cast from const char * to
const
> > char *&

<snip>

>
> The standard does not let you pass a non-const reference to a
temporary
> object.

<snip>

> then the code will compile cleanly.
> (There are some compilers which are more tolerant than others about
> compiling code
> which passes non-const references to temopraries - some compile with
a
> warning and
> others compile silently.)

I ran into a related problem yesterday: I was trying to pass the
result of a function returning char*& to a function taking const
char*&. GCC3.2+, Comeau 4.3.3 and Codewarrior 8.3 complained about
binding a non-const reference to a temporary, while VC7.1 and Intel
7.1-8.0 compiled without complaint.

I believe GCC et. al are correct, since the conversion from char* to
const char* yields an rvalue. (Is this right?) I ended up using a
const_cast to *add* const-qualification.

Interestingly, rather than allowing a non-const reference to bind to a
temporary, VC7.1 and Intel apparently did not generate a temporary at
all, since the code they generated executed correctly, which it could
not have done if a temporary had been passed.

Jonathan


---
[ 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: jpotter@falcon.lhup.edu (John Potter)
Date: Tue, 30 Mar 2004 18:24:39 +0000 (UTC)
Raw View
On Tue, 30 Mar 2004 05:04:57 +0000 (UTC), ntalsani@rochester.rr.com
wrote:

> I am trying to figure out which compiler is providing the proper behavior in
> the following code which has an implicit cast from const char * to const
> char *&

That is not a cast, but an initialization.  A reference to non-const may
not be initialized by an rvalue.

> #include <iostream>
> #include <string>
> void tester(const char *&parm)

If you change this to (char const* const& parm) it will be valid.  The
movement of the first const is just style, the second one makes the code
valid.

> {std::cout << parm << std::endl;}
>
> int main()
> {
>     std::string foo = "test string";
>     tester(foo.c_str());

You could also change the call if the reference to non-const is
required.

   char const* p(foo.c_str());
   tester(p); // Now called using an lvalue

> }

> Under one complier, this code compiles and runs just fine. Under a different
> compiler, I get a compiler error stating something (I dont have access to
> that compiler from here) about needing an lvalue to convert from const char
> * to const char *&.

The conversion is confusion, but the lvalue is needed.

> I dont have ready access to a copy of the standard to see what it says on
> the matter, so I was hoping that someone could let me know which compiler
> was correct (and why so that when I tell my coworker which compiler is right
> I can explain it to him. )

See the messy 8.5.3/5.  When you have an rvalue initializer for a
reference, the reference must be to const.

John

---
[ 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: thp@cs.ucr.edu
Date: Tue, 30 Mar 2004 18:24:56 +0000 (UTC)
Raw View
ntalsani@rochester.rr.com wrote:
+ I am trying to figure out which compiler is providing the proper behavior in
+ the following code which has an implicit cast from const char * to const
+ char *&
+
+ Here is the sample code:
+
+ #include <iostream>
+ #include <string>
+ void tester(const char *&parm)
+ {std::cout << parm << std::endl;}
+
+ int main()
+ {
+    std::string foo = "test string";
+    tester(foo.c_str());
+ }
+
+ Under one complier, this code compiles and runs just fine. Under a different
+ compiler, I get a compiler error stating something (I dont have access to
+ that compiler from here) about needing an lvalue to convert from const char
+ * to const char *&.
+
+ I dont have ready access to a copy of the standard to see what it says on
+ the matter, so I was hoping that someone could let me know which compiler
+ was correct (and why so that when I tell my coworker which compiler is right
+ I can explain it to him. )

As you know, foo.c_str() returns a char* rvalue, which can't be passed
by reference, but which can be passed by const reference -- the
implementnation creates a char* temporary to hold the char* rvalue thus
creating an lvalue from it.  So try:


 #include <iostream>
 #include <string>
 void tester(const char * const & parm)
 {std::cout << parm << std::endl;}

 int main()
 {
    std::string foo = "test string";
    tester(foo.c_str());
 }

since you aren't attempting to modify the char* that you pass by
reference.

Tom Payne

---
[ 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: ahp6@email.byu.edu ("Adam H. Peterson")
Date: Wed, 31 Mar 2004 00:16:46 +0000 (UTC)
Raw View
> I ran into a related problem yesterday: I was trying to pass the
> result of a function returning char*& to a function taking const
> char*&. GCC3.2+, Comeau 4.3.3 and Codewarrior 8.3 complained about
> binding a non-const reference to a temporary, while VC7.1 and Intel
> 7.1-8.0 compiled without complaint.

The code is incorrect.  Allowing a const char *& to bind to a char *
lvalue can circumvent const safety.

>
> I believe GCC et. al are correct, since the conversion from char* to
> const char* yields an rvalue. (Is this right?) I ended up using a
> const_cast to *add* const-qualification.

But you aren't converting a char* to a const char* (which would be
okay).  You are converting a char*& (which is an lvalue) to a const
char*&, an lvalue conversion.

>
> Interestingly, rather than allowing a non-const reference to bind to a
> temporary, VC7.1 and Intel apparently did not generate a temporary at
> all, since the code they generated executed correctly, which it could
> not have done if a temporary had been passed.

The fact that they didn't generate a temporary is a tip-off that const
safety could be violated.  Consider:

char c=0;
char const cc=1;
char *pc=&c;
char const *pcc=&cc;
char *&rpc=pc;
char const *&rpcc=rpc; // This line is not allowed...
rpcc=pcc; // ... because it allows this line ...
*pc=2; // ... which causes this line ...
// ... to change cc, which invokes UB.

---
[ 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: technews@kangaroologic.com ("Jonathan Turkanis")
Date: Wed, 31 Mar 2004 19:10:21 +0000 (UTC)
Raw View
""Adam H. Peterson"" <ahp6@email.byu.edu> wrote in message
news:c4ckoa$9s3u$1@acs2.byu.edu...
> > I ran into a related problem yesterday: I was trying to pass the
> > result of a function returning char*& to a function taking const
> > char*&. GCC3.2+, Comeau 4.3.3 and Codewarrior 8.3 complained about
> > binding a non-const reference to a temporary, while VC7.1 and
Intel
> > 7.1-8.0 compiled without complaint.
>
> The code is incorrect.  Allowing a const char *& to bind to a char *
> lvalue can circumvent const safety.
>
> >
> > I believe GCC et. al are correct, since the conversion from char*
to
> > const char* yields an rvalue. (Is this right?) I ended up using a
> > const_cast to *add* const-qualification.
>
> But you aren't converting a char* to a const char* (which would be
> okay).  You are converting a char*& (which is an lvalue) to a const
> char*&, an lvalue conversion.

So you agree that the code is incorrect, but believe my diagnosis is
wrong? You believe that the compiler diagnostics about binding
non-const references to temporaries are spurious; instead, the
prohibition is entirely related to const-correctness?

<snip>

>
> The fact that they didn't generate a temporary is a tip-off that
const
> safety could be violated.  Consider:
>
> char c=0;
> char const cc=1;
> char *pc=&c;
> char const *pcc=&cc;
> char *&rpc=pc;
> char const *&rpcc=rpc; // This line is not allowed...
> rpcc=pcc; // ... because it allows this line ...
> *pc=2; // ... which causes this line ...
> // ... to change cc, which invokes UB.

Yeah, I've seen this before, but forgot it. The two-levels of
indirection confuses me.

Fortunately, I know that in my case there is no violation of
const-correctness, even with the const-cast, but I'd like to design
the interface of the function so it can be called naturally without a
const-cast.

The function in question is almost identical to the std::codecvt
members in and out:

    void f(const char*& in_begin, const char* in_end, char*&
out_begin, char* out_end);

It's supposed to consume zero or more characters form the input
sequence, which it signifies by advancing in_begin, and output zero or
more characters to the output sequence, which it signifies by
advancing out_begin. The pointers delimiting the input sequence are to
const char to signify that the data should not be modified; the
pointers delimiting the output sequence are to non-const char, because
these characters should be modified.

This interface seems reasonable, doesn't it?

-----

Now suppose I have a buffer of size n:

    char* buf;

It follows from the above that I'm not allowed to invoke f as follows:

    f(buf, buf + n, ....)

right? To call f, I have to do

   f(const_cast<const char*&>(buf), cbuf + n, ...);

or

    const char* cbuf = ptr;
    f(cbuf , cbuf + n, ...);

That's a lot of work just to invoke a function with a seemingly
reasonable interface. What's gone wrong here?

Jonathan






---
[ 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: brok@rubikon.pl (Bronek Kozicki)
Date: Wed, 31 Mar 2004 19:15:52 +0000 (UTC)
Raw View
On Wed, 31 Mar 2004 00:16:46 +0000 (UTC), "Adam H. Peterson" wrote:
>> I believe GCC et. al are correct, since the conversion from char* to
>> const char* yields an rvalue. (Is this right?) I ended up using a
>> const_cast to *add* const-qualification.
>
> But you aren't converting a char* to a const char* (which would be
> okay).  You are converting a char*& (which is an lvalue) to a const
> char*&, an lvalue conversion.


There is no such thing. What Jonathan was trying to do was to bind
lvalue to rvalue of non reference-compatible type, which is illegal per
clause 8.5.3. Why rvalue? Because there is conversion from "char *" to
"const char *" first, and result of this conversion (just like result of
any other standard conversion - covered by clause 4) is temporary
rvalue. Next he's trying to bind this rvalue to reference, which is
illegal for reasons stated above. I understand that there might some
doubts as to why not bind "char *" (an lvalue) directly to
"const char *&" - for explanation see definition of reference-compatible
types in 8.5.3/4 and qualification conversions in 4.4. You will find
that difference in CV-qualification of pointee requires this conversion,
thus resulting in temporary rvalue.

To make things more clear core language issue 434 has been proposed, see
http://std.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#434


B.

---
[ 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: technews@kangaroologic.com ("Jonathan Turkanis")
Date: Wed, 31 Mar 2004 20:56:13 +0000 (UTC)
Raw View
"Bronek Kozicki" <brok@rubikon.pl> wrote in message
news:wqvyatnvdf8q$.xjjq5v0kox8m.dlg@40tude.net...
> On Wed, 31 Mar 2004 00:16:46 +0000 (UTC), "Adam H. Peterson" wrote:
> >> I believe GCC et. al are correct, since the conversion from char*
to
> >> const char* yields an rvalue. (Is this right?) I ended up using a
> >> const_cast to *add* const-qualification.

<snip>

> There is no such thing. What Jonathan was trying to do was to bind
> lvalue to rvalue of non reference-compatible type, which is illegal
per
> clause 8.5.3. Why rvalue? Because there is conversion from "char *"
to
> "const char *" first, and result of this conversion (just like
result of
> any other standard conversion - covered by clause 4) is temporary
> rvalue. Next he's trying to bind this rvalue to reference, which is
> illegal for reasons stated above.

Hi Bronek,

Let me see if you agree with this. Consider:

    char*& source();
    void sink(const char*&);
    void sink2(const char* const&);

    int main()
    {
        sink(source());  // error.
        sink2(source());  // okay.
    }

The first bullet of 8.5.3/5 does not apply, because of reference
incompatibility; the second bullet ('--Otherwise') does not apply,
since const char* is non-const.

The compiler diagnostic about binding non-const references to
temporaries comes from attempting to bind the result of converting the
resturn value of source() to const char*. However, the compiler could
just as reasonably have complained about violation of the condition in
the first bullet. Right?

I'm still having trouble understanding why the code above is unsafe.

> I understand that there might some
> doubts as to why not bind "char *" (an lvalue) directly to
> "const char *&" - for explanation see definition of
reference-compatible
> types in 8.5.3/4 and qualification conversions in 4.4. You will find
> that difference in CV-qualification of pointee requires this
conversion,
> thus resulting in temporary rvalue.
>
> To make things more clear core language issue 434 has been proposed,
see
> http://std.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#434

Interesting.

Regards,
Jonathan


---
[ 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: brok@rubikon.pl (Bronek Kozicki)
Date: Thu, 1 Apr 2004 08:57:41 +0000 (UTC)
Raw View
On Wed, 31 Mar 2004 19:10:21 +0000 (UTC), "Jonathan Turkanis" wrote:
>     void f(const char*& in_begin, const char* in_end, char*&
> out_begin, char* out_end);


> Now suppose I have a buffer of size n:
>
>     char* buf;
>
> It follows from the above that I'm not allowed to invoke f as follows:
>
>     f(buf, buf + n, ....)
>
> right? To call f, I have to do
>
>    f(const_cast<const char*&>(buf), cbuf + n, ...);
>
> or
>
>     const char* cbuf = ptr;
>     f(cbuf , cbuf + n, ...);
>
> That's a lot of work just to invoke a function with a seemingly
> reasonable interface. What's gone wrong here?

Hello Jonathan

const_cast is dangerous. If you see need for it, probably there's
something wrong with code. Consider this:

#include <cstring>
#include <cstdio>

void f(const char *&p)
{
  const char *c = "dead";
  printf("%s\t%p\n", p, p);
  p = c;
}

int main()
{
  static const int size = 5;
  char *p = new char[size];
  strncpy(p, "abcd", size);

  // f(p); invalid initialization of non-const reference
  f(const_cast<const char*&> (p)); //danger

  printf("%s\t%p\n", p, p);
  strncpy(p, "beef", size); //boom
  printf("%s\t%p\n", p, p);
}

Here const_cast has been used the same way you are going to obey
limitation of non-const reference initialization in your example cited
above. This reference is used later to circumvent constness of another
variable. There are few compilers (eg. all versions of Visual C++, when
started without option /Za) that will accept line I have commented out:
  f(p);
due to this compiler err^H non-conformance issue above code will violate
const variable without single const_cast.

I think that you should remove reference from first parameter of your
function:

void f(const char* in_begin, const char* in_end, char*& out_begin /* */)

this will make your function easy and safe to use. Alternatively you may
use const reference:

void f(const char* const& in_begin, const char* in_end, /* ... */)


Regards


B.

---
[ 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: brok@rubikon.pl (Bronek Kozicki)
Date: Thu, 1 Apr 2004 10:35:21 +0000 (UTC)
Raw View
On Wed, 31 Mar 2004 20:56:13 +0000 (UTC), "Jonathan Turkanis" wrote:

>     char*& source();
[...]
>     void sink(const char*&);
[...]
>         sink(source());  // error.

> The first bullet of 8.5.3/5 does not apply, because of reference
> incompatibility; the second bullet ('--Otherwise') does not apply,
> since const char* is non-const.

Right. Actually the only part of C++ standard that applies here is last
sentence of this clause: "otherwise, the program is ill-formed" (just
before final example and note).

> The compiler diagnostic about binding non-const references to
> temporaries comes from attempting to bind the result of converting the
> resturn value of source() to const char*.

Result of such conversion cannot be bound, because rvalue cannot be
bound to non-const reference; and result of any standard conversion is
rvalue. If some compiler tries to perform conversion and then finds that
result of such conversion cannot be bound to reference it's OK, but it's
equally valid to reject code due to first bullet of clause 8.5.3/5 of
C++ standard, as there is no other bullet which could allow binding to
non-const reference. It's just quality of implementation issue, but (as
ill-formed code has been rejected by compiler) result is the same, as
mandated by said clause. Actually Comeau compiler issues following error
message:

"T.cpp", line 25: error #434: a reference of type "const char *&" (not
  const-qualified) cannot be initialized with a value of type "char *"
   sink(source());  // error.


> However, the compiler could
> just as reasonably have complained about violation of the condition in
> the first bullet. Right?

You got it right :)

> I'm still having trouble understanding why the code above is unsafe.

See my other message in this thread. Best regards


B.

---
[ 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                       ]