[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c++

Using std::string by reference

James Moe

6/12/2016 7:48:00 PM


gcc 4.7.3

std::string UtilsSetPathSep2 (std::string & str, const char *
path_sep_char_new)
{ ... }

When passing the argument "str" by reference, "std::string &", this
error is emitted:

pmmsend.c: In function 'pmmsend_return_code_t program_args(int, char**,
prog_args_t&)':
pmmsend.c:449:63: error: invalid initialization of reference of type
'std::string& {aka std::basic_string<char>&}' from expression of type
'char*'
pmmsend.c:287:8: error: in passing argument 1 of 'std::string
UtilsSetPathSep2(std::string&, const char*)'

When "std::string &" is changed to "std::string", no error(s) result.

Is it not possible to use std::string by reference?

--
James Moe
jmm-list at sohnen-moe dot com
9 Answers

Ian Collins

6/12/2016 7:57:00 PM

0

On 06/13/16 07:47 AM, James Moe wrote:
>
> gcc 4.7.3
>
> std::string UtilsSetPathSep2 (std::string & str, const char *
> path_sep_char_new)
> { ... }
>
> When passing the argument "str" by reference, "std::string &", this
> error is emitted:
>
> pmmsend.c: In function 'pmmsend_return_code_t program_args(int, char**,
> prog_args_t&)':
> pmmsend.c:449:63: error: invalid initialization of reference of type
> 'std::string& {aka std::basic_string<char>&}' from expression of type
> 'char*'
> pmmsend.c:287:8: error: in passing argument 1 of 'std::string
> UtilsSetPathSep2(std::string&, const char*)'
>
> When "std::string &" is changed to "std::string", no error(s) result.
>
> Is it not possible to use std::string by reference?

Yes, but in this case you are passing a char* which will create a
temporary string. You can't bind a temporary object to a reference, so
you will have to use const std::string&, std::string or std::string&& as
the parameter type in this case.

--
Ian

Alf P. Steinbach /Usenet

6/12/2016 11:29:00 PM

0

On 12.06.2016 21:47, James Moe wrote:
>
> std::string UtilsSetPathSep2 (std::string & str, const char *
> path_sep_char_new)
> { ... }
>
> When passing the argument "str" by reference, "std::string &", this
> error is emitted:
>
> pmmsend.c: In function 'pmmsend_return_code_t program_args(int, char**,
> prog_args_t&)':
> pmmsend.c:449:63: error: invalid initialization of reference of type
> 'std::string& {aka std::basic_string<char>&}' from expression of type
> 'char*'

This means that you have tried to use a `char*` or a `char` array as
actual argument for the `std::string&` formal argument.

Since `std::string&` is a reference-to-non-`const`, where the function
can /modify/ the actual argument object, you need an actual argument of
the exact same type or a class derived from that type (in the latter
case the reference is bound to the `std::string` base class sub-object).

The function can't very well modify an instance of some other type as if
it were a `std::string`.


> pmmsend.c:287:8: error: in passing argument 1 of 'std::string
> UtilsSetPathSep2(std::string&, const char*)'
>
> When "std::string &" is changed to "std::string", no error(s) result.

Because now the argument is passed by value, which means that the actual
argument is copied or converted, or the behavior is /as if/ there is a
copying or conversion.

And `std::string` provides a constructor that accepts a `char const*`.
I.e. you get an automatic conversion.

The function's code now operates on a copy or an instance resulting from
a conversion, and so it can't affect the original argument, in your
specific case the `char` array.


> Is it not possible to use std::string by reference?

That's possible.

Since the function /returns/ a `std::string` you probably wanted
`std::string const&` anyway. :-)


Cheers & hth.,

- Alf

K. Frank

6/13/2016 1:41:00 PM

0

Hi Ian!

On Sunday, June 12, 2016 at 3:57:15 PM UTC-4, Ian Collins wrote:
> On 06/13/16 07:47 AM, James Moe wrote:
> > ...
> > pmmsend.c:449:63: error: invalid initialization of reference of type
> > 'std::string& {aka std::basic_string<char>&}' from expression of type
> > 'char*'
> > ...
>
> Yes, but in this case you are passing a char* which will create a
> temporary string. You can't bind a temporary object to a reference, so
> you will have to use const std::string&, std::string or std::string&& as
> the parameter type in this case.

Just to be sure I understand:

The language could have permitted passing a temporary as
a non-cost reference, f (std::string &str);, but most of
the time you would be making a mistake doing this, so the
language prohibits it.

In this case -- passing in a temporary -- by using a
non-const rvalue reference, f (std::string &&str), you
are really just telling the compiler that you do want
to do this, and are not making a mistake.

That is, the code generated when binding a temporary
to a non-cost rvalue reference is the same as what
one would have expected (okay, what I would have expected)
if binding to a non-cost lvalue reference has been
allowed.

Is this correct, or am I missing something important
about this specific use case?

> Ian


Thanks for any clarification.


K. Frank

Öö Tiib

6/13/2016 5:31:00 PM

0

On Monday, 13 June 2016 16:40:56 UTC+3, K. Frank wrote:
> Hi Ian!
>
> On Sunday, June 12, 2016 at 3:57:15 PM UTC-4, Ian Collins wrote:
> > On 06/13/16 07:47 AM, James Moe wrote:
> > > ...
> > > pmmsend.c:449:63: error: invalid initialization of reference of type
> > > 'std::string& {aka std::basic_string<char>&}' from expression of type
> > > 'char*'
> > > ...
> >
> > Yes, but in this case you are passing a char* which will create a
> > temporary string. You can't bind a temporary object to a reference, so
> > you will have to use const std::string&, std::string or std::string&& as
> > the parameter type in this case.
>
> Just to be sure I understand:
>
> The language could have permitted passing a temporary as
> a non-cost reference, f (std::string &str);, but most of
> the time you would be making a mistake doing this, so the
> language prohibits it.

The language could have permitted taking l-value reference to
r-value or taking r-value reference to l-value but it would be
misleading most of the time. The differences are complexity
that can give benefit in optimizing or mix everything up and
confuse.

It is somewhat similar taking pointer to non-const to string literal:

char* p = "wrong";

For legacy reasons we get only warning about that from most
of the compilers and if we don't attempt to modify *p then
it works fine.

>
> In this case -- passing in a temporary -- by using a
> non-const rvalue reference, f (std::string &&str), you
> are really just telling the compiler that you do want
> to do this, and are not making a mistake.

Yes, with that const we have 4 references:

Reference to l-value of type X is 'X&'.
Reference to r-value of type X is 'X&&'.
Reference to const of type X is 'X const&'.
Thing we seemingly need for nothing is 'X const&&'.

>
> That is, the code generated when binding a temporary
> to a non-cost rvalue reference is the same as what
> one would have expected (okay, what I would have expected)
> if binding to a non-cost lvalue reference has been
> allowed.

It is hard to know what you would had expected. Reference is
meant to be alias name to some value so compiler is not required
to generate any code about such alias. If a reference is stored
somewhere (for example class member) then to there it goes
most likely as memory address but it is nowhere required. Same
is with pointer, it is not specified what its bits mean.

>
> Is this correct, or am I missing something important
> about this specific use case?

Programming languages are meant for describing behavior of
programs and are not for instructing processors. That is the
sole little step we have ever taken above assemblers.


James Moe

6/13/2016 6:57:00 PM

0

On 06/12/2016 12:57 PM, Ian Collins wrote:
>> > Is it not possible to use std::string by reference?
> Yes, but in this case you are passing a char* which will create a
> temporary string. You can't bind a temporary object to a reference, [...]
>
Ah, I understand.
Thank you.

--
James Moe
jmm-list at sohnen-moe dot com

K. Frank

6/13/2016 8:42:00 PM

0

Hello Öö!

On Monday, June 13, 2016 at 1:30:45 PM UTC-4, Öö Tiib wrote:
> On Monday, 13 June 2016 16:40:56 UTC+3, K. Frank wrote:
> > Hi Ian!
> >
> > On Sunday, June 12, 2016 at 3:57:15 PM UTC-4, Ian Collins wrote:
> > > On 06/13/16 07:47 AM, James Moe wrote:
> > > > ...
> > > > pmmsend.c:449:63: error: invalid initialization of reference of type
> > > > 'std::string& {aka std::basic_string<char>&}' from expression of type
> > > > 'char*'
> > > > ...
> > Just to be sure I understand:
> >
> > The language could have permitted passing a temporary as
> > a non-cost reference, f (std::string &str);, but most of
> > the time you would be making a mistake doing this, so the
> > language prohibits it.
> ...
> > That is, the code generated when binding a temporary
> > to a non-cost rvalue reference is the same as what
> > one would have expected (okay, what I would have expected)
> > if binding to a non-cost lvalue reference has been
> > allowed.
>
> It is hard to know what you would had expected.

Here's a not-too-badly-contrived example to illustrate
what I would have expected:

void translateInPlaceAndPrint (std::string &str) {
if (str == "Good Morning!") str == "Guten Morgan!";
std::cout << str << std::endl;
}

// use case 1 -- won't compile
translateInPlaceAndPrint ("Good Morning!");

// use case 1 -- work-around
std::string tmp = "Good morning!";
translateInPlaceAndPrint (tmp);

// use case 2
std::string useMoreThanOnce = "Good Morning!";
translateInPlaceAndPrint (useMoreThanOnce);
// print again just for fun
std::cout << useMoreThanOnce << std::endl;

If "// use case 1 -- won't compile" were permitted, what
I would expect would be:

1) A temporary std::string is constructed on the stack
from "Good Morning!"
2) The temporary is bound to the non-const reference
argument
3) The lifetime of the temporary is extended to when
the function call returns
4) The function modifies its (non-const reference)
argument in place (and prints it out)
5) The function returns, the temporary's extended
lifetime ends, and the temporary is destroyed

This seems quite reasonable, and seems to me to be what
most people would expect if this were actually permitted.

It's certainly not necessary to permit this -- the
work-around is easy and obvious. (And the work-around
is almost the same as my hypothetical "expected"
behavior. The only difference is that the lifetime
of tmp is to the end of its block scope, rather than
just to when the function returns.)

> ...
> > Is this correct, or am I missing something important
> > about this specific use case?
>
> Programming languages are meant for describing behavior of
> programs and are not for instructing processors. That is the
> sole little step we have ever taken above assemblers.

Well, yes. And it is true that forbidding the binding of
temporaries to non-const references does prevent a certain
class of errors.

But the design of c++ has generally been to be
self-consistently flexible, rather than preventing you
from shooting yourself in the foot.

It just seems to me that permitting the binding of a
temporary to non-cost reference would be very much in
the spirit of c++, and would work just fine if implemented
as I described above, unless there is some other set
of problems I've overlooked.

I'm not proposing that the language permit this. I'm
just asking whether the language could have easily
permitted this (for example, as outlined above), or
whether I'm missing something and trying to permit this
would generate a whole other set of complications that
I've overlooked.


Thanks for any further enlightenment.


K. Frank

Paavo Helde

6/13/2016 9:30:00 PM

0

On 13.06.2016 16:40, K. Frank wrote:
> Hi Ian!
>
> On Sunday, June 12, 2016 at 3:57:15 PM UTC-4, Ian Collins wrote:
>> On 06/13/16 07:47 AM, James Moe wrote:
>>> ...
>>> pmmsend.c:449:63: error: invalid initialization of reference of type
>>> 'std::string& {aka std::basic_string<char>&}' from expression of type
>>> 'char*'
>>> ...
>>
>> Yes, but in this case you are passing a char* which will create a
>> temporary string. You can't bind a temporary object to a reference, so
>> you will have to use const std::string&, std::string or std::string&& as
>> the parameter type in this case.
>
> Just to be sure I understand:
>
> The language could have permitted passing a temporary as
> a non-cost reference, f (std::string &str);, but most of
> the time you would be making a mistake doing this, so the
> language prohibits it.

Yes it could, and earlier MSVC versions indeed allowed it for a long
time, at least in some situations.

I believe this was prohibited this feature was extremely fragile. Make a
innocent-looking change in the code like replacing an int with a long,
and the program would silently start misbehaving because a temporary
would be created where there was none before, or vice versa.

>
> In this case -- passing in a temporary -- by using a
> non-const rvalue reference, f (std::string &&str), you
> are really just telling the compiler that you do want
> to do this, and are not making a mistake.
>
> That is, the code generated when binding a temporary
> to a non-cost rvalue reference is the same as what
> one would have expected (okay, what I would have expected)
> if binding to a non-cost lvalue reference has been
> allowed.

In some sense, this is correct, a temporary would be created and its
address passed to the function. What is different are the assumptions
what the called function would make: a temporary cannot be used for
transporting information out of the function, whereas non-const lvalue
references are often used exactly for that purpose.

So from the point of the overall functionality the two scenarios
(calling with a non-const lvalue reference and calling with a temporary)
differ drastically; using the same syntax for two drastically different
things would not be a good design.

Cheers
Paavo


ram

6/13/2016 10:13:00 PM

0

kfrank29.c@gmail.com writes:
> void translateInPlaceAndPrint (std::string &str) {
> if (str == "Good Morning!") str == "Guten Morgan!";
> std::cout << str << std::endl;
> }

Nowhere is »str« being assigned to above,
so it can be declared as »const«.

scott

6/13/2016 11:36:00 PM

0

ram@zedat.fu-berlin.de (Stefan Ram) writes:
>kfrank29.c@gmail.com writes:
>> void translateInPlaceAndPrint (std::string &str) {
>> if (str == "Good Morning!") str == "Guten Morgan!";
>> std::cout << str << std::endl;
>> }
>
> Nowhere is »str« being assigned to above,
> so it can be declared as »const«.
>

Seems a bit pedantic on your part, you could simply have
pointed out that his attemped assignment was incorrect as
it is clear from the function name what his intent was.

>> // use case 1 -- won't compile
>> translateInPlaceAndPrint ("Good Morning!");

Regardless, K. Frank shouldn't expect the lifetime of the temp string object to
extend beyond the function call in his example #1, so it
would be pointless for the compiler to allow it.