I think I mostly understand l-values and r-values after reading a few
archived posts on this board:
* l-values usually appear on the LHS of an assignment, r-values
usually appear on the RHS of an assignment
An r-value might appear on the LHS of an assignment because of the
following:
* C++ allows a member function to be invoked on a temporary object
* A function returning by value is an r-value
* The assignment operator can be a member function of a UDT
The combination of these factors means that an r-value can appear on
the LHS of an assignment operator:
Foo GetTemp() {return Foo;}
GetTemp() = 5; // which is equivalent to
GetTemp.operator=(5); //
(this of course assumes that the appropriate conversion constructors
and assignment operators are provided for class Foo)
However, I still don't understand how a string literal can be
considered an l-value. The following excerpts hint at an answer:
"String literal: because early C did not have 'const', and so old C
functions that take 'char*' as argument could not be called with
literal strings as actual arguments if string literals were considered
rvalues. However, that may still change. It's just an old
compatibility feature on its way out" - http://tiny.cc/yEr2A
"char *x and char x[] are often used to refer to strings (null
terminated arrays of characters), and they differ in their "lvalue-
ness". " - http://tiny.cc/9JJBG
Wouldn't the following:
void foo(char *);
foo("Hello");
be equivalent to:
char * blah = "Hello";
foo(blah);
? I don't see how this effects the l-valueness of a string literal
Also, is it a defining characteristic of an r-value that you can not
take the address of an r-value (obviously this is true for most cases,
I was wondering if it is, in fact, a defining characteristic, or
whether there are some exceptions)
Taras
> Hi everyone,
>
> I think I mostly understand l-values and r-values after reading a few
> archived posts on this board:
>
> * l-values usually appear on the LHS of an assignment, r-values
> usually appear on the RHS of an assignment
Modifiable lvalues may appear on the LHS of an assignment. Most types
of lvalue can appear on the RHS of an assignment, modifiable or nor.
When this occurs, an lvalue to rvalue conversion is performed.
> An r-value might appear on the LHS of an assignment because of the
> following:
>
> * C++ allows a member function to be invoked on a temporary object
> * A function returning by value is an r-value
> * The assignment operator can be a member function of a UDT
>
> The combination of these factors means that an r-value can appear on
> the LHS of an assignment operator:
>
> Foo GetTemp() {return Foo;}
>
> GetTemp() = 5; // which is equivalent to
> GetTemp.operator=(5); //
>
> (this of course assumes that the appropriate conversion constructors
> and assignment operators are provided for class Foo)
>
> However, I still don't understand how a string literal can be
> considered an l-value. The following excerpts hint at an answer:
Do you understand the actual definition of an lvalue? An lvalue
refers to an object or function. Do you understand the definition of
an object? An object is a region of storage. So a reference to
anything that occupies storage is a valid lvalue.
> "String literal: because early C did not have 'const', and so old C
> functions that take 'char*' as argument could not be called with
> literal strings as actual arguments if string literals were considered
> rvalues. However, that may still change. It's just an old
> compatibility feature on its way out" - http://tiny.cc/yEr2A
>
> "char *x and char x[] are often used to refer to strings (null
> terminated arrays of characters), and they differ in their "lvalue-
> ness". " - http://tiny.cc/9JJBG
This second link is to a very old post, and is quite wrong.
"An lvalue is more a C term than a C++ term. It refers to a value
that can appear on the left (hence "l") side of an assignment."
Wrong, the term lvalue is specifically defined and widely used in the
C++ standard.
"char *x and char x[] are often used to refer to strings (null
terminated arrays of characters), and they differ in their
"lvalue-ness". I point this out because I'm guessing that this
difference may be at the root of your question."
Wrong.
> Wouldn't the following:
>
> void foo(char *);
>
> foo("Hello");
The line above creates an lvalue, an unnamed array of 6 constant
characters. They exist somewhere in memory and have static storage
duration, coming into existence before main() is called, and enduring
until after main() ends execution.
> be equivalent to:
>
> char * blah = "Hello";
The line above creates two lvalues, the first is a pointer to char
'blah', and the second is an unnamed array of 6 constant chars.
> foo(blah);
>
> ? I don't see how this effects the l-valueness of a string literal
It doesn't, the post you quoted, from more than 14 years ago, is
wrong.
> Also, is it a defining characteristic of an r-value that you can not
> take the address of an r-value (obviously this is true for most cases,
> I was wondering if it is, in fact, a defining characteristic, or
> whether there are some exceptions)
>
> Taras
There are some cases where you can take the address of an rvalue, but
there is not much you can actually do with it.
--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
This might be referring to differences between the actual types
"char*" and the type "char[]". For example:
char *x;
x = "Something"; // This is valid (although unsafe, see below).
char y[10];
y = "Something"; // This is not allowed. Note y is on LHS.
That's not to say you can't convert both to a char *:
char *z;
char *x = NULL;
z = x; // This is valid.
char y[10];
z = y; // This is also valid. Note y is on RHS.
And that's the same as this:
void foo (char *z);
char *x = NULL;
foo(x); // This is valid.
char y[10];
foo(y); // This conversion is also valid.
> Wouldn't the following:
>
> void foo(char *);
>
> foo("Hello");
>
> be equivalent to:
>
> char * blah = "Hello";
> foo(blah);
Also note that this is a special case; string literals are able to be
converted to non-const just to main compatibility with old code, but
aren't guaranteed to be in a modifiable part of memory. Therefore:
char *blah = "Hello";
blah[0] = 'J';
Is officially undefined. Whereas:
char array[] = "Hello";
char *blah = array;
blah[0] = 'J';
Is perfectly safe.
In general you should not try to write new code that takes string
literals as non-const char*. Perhaps consider this instead:
void foo (const char *);
const char *blah = "Hello";
foo(blah);
I.e. You should never write a line of code like this:
char *blah = "Hello";
Jason
> * l-values usually appear on the LHS of an assignment, r-values
> usually appear on the RHS of an assignment
Historically, that's where the distinction (and the names) come
from. Today, I'd say that the concept is far more arbitrary;
some operators return rvalues, others lvalues and some require
rvalues, others lvalues. And the only real definition of the
terms is the list of these requirements.
> An r-value might appear on the LHS of an assignment because of
> the following:
> * C++ allows a member function to be invoked on a temporary object
> * A function returning by value is an r-value
> * The assignment operator can be a member function of a UDT
> The combination of these factors means that an r-value can
> appear on the LHS of an assignment operator:
Yes, because a user defined assignment operator isn't an
assignment operator. Once overload resolution has chosen it,
it is a function call, and it obeys the rules of function calls,
not the rules of assignment operators.
> Foo GetTemp() {return Foo;}
> GetTemp() = 5; // which is equivalent to
> GetTemp.operator=(5); //
You mean
GetTemp().operator=( 5 ) ;
for the second line, of course.
> (this of course assumes that the appropriate conversion
> constructors and assignment operators are provided for class
> Foo)
> However, I still don't understand how a string literal can be
> considered an l-value.
It's an array, and all arrays are l-values.
> The following excerpts hint at an answer:
> "String literal: because early C did not have 'const', and so
> old C functions that take 'char*' as argument could not be
> called with literal strings as actual arguments if string
> literals were considered rvalues. However, that may still
> change. It's just an old compatibility feature on its way
> out" -http://tiny.cc/yEr2A
This has nothing to do with lvalue-ness. In C, the type of a
string literal is char[]. In C++, it is char const[], but there
is a special, limited (and I think deprecated) conversion to
char*, to avoid breaking existing code.
> "char *x and char x[] are often used to refer to strings (null
> terminated arrays of characters), and they differ in their
> "lvalue- ness". " -http://tiny.cc/9JJBG
This is just wrong. In both cases, x is an lvalue. They
differ in type, not in lvalue-ness.
> Wouldn't the following:
> void foo(char *);
> foo("Hello");
> be equivalent to:
> char * blah = "Hello";
> foo(blah);
> ? I don't see how this effects the l-valueness of a string
> literal
Yes, and it doesn't.
> Also, is it a defining characteristic of an r-value that you
> can not take the address of an r-value (obviously this is true
> for most cases, I was wondering if it is, in fact, a defining
> characteristic, or whether there are some exceptions)
Well, it's certainly related to the original sense: a lvalue
always designated an object, an rvalue (originally) was just a
value, without an object. In C++, if the rvalue has class type,
then there is an object (and you can obtain its address). Also,
you can initialize a reference to const with an rvalue, and then
take its address. The result is that the distinction
lvalue/rvalue is purely arbitrary.
--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Stroustrup states that: "there is a need for a name for "something in
memory." This is the simplest and most fundamental notion of an
object. That is, an /object/ is a contiguous region of storage; an /
lvalue/ is an expression that refers to an object"
But in a sense, the '5' in the expression:
int i = 5;
would be stored somewhere in memory (ultimately, unless it is
optimised out). I suppose that '5' can not be an l-value, because it
isn't an expression that refers to an object (it actually is the
object itself).
Additionally, K&R states that: "An object is a manipulatable region of
storage; an lvalue is an expression referring to an object"
I suppose with that in mind you can't really modify the value of 5, so
with this definition '5' isn't an object.
I'm thinking of an r-value more like a 'concept' rather than a an
object, if that makes any sense. You can not change the 'concept' of
the number 5, 5 will always be 5. This is re-inforced in C++ by only
allowing to take a constant reference to temporaries:
const int & rr = 3;
Otherwise, you could do something like this:
rr = 4;
which would then change 3 to be equal to 4, which doesn't make sense!
That's why I stated 'usually' :)
"The type of a string literal is ‘‘array of the appropriate number of
c o n s t characters,’’ so " B o h r " is
of type c o n s t c h a r []"
So thus a string literal is an l-value.
Your responses say that a string literal is static and in a non-
modifiable area of memory. Also, Bjarne writes "A string literal is
statically allocated so that it is safe to return one from a function"
Is this in any way similar to the Java implementation of Strings,
where a string is immutable and contained within a string pool (I
realise that this is a c++ group, so apologies), in that in C++ the
same string may refer to the same memory location (this is
implementation defined) - assigning two literal strings that are the
same to different pointers is conceptually similar to assigning the
integer 5 to two different variables? Was there any reason apart from
efficiency than choosing this meaning rather than 'each string literal
creates a separate object'?
Taras
Non-modifiable, IIUIC.
>
> Your responses say that a string literal is static and in a non-
> modifiable area of memory. Also, Bjarne writes "A string literal is
> statically allocated so that it is safe to return one from a function"
>
> Is this in any way similar to the Java implementation of Strings,
> where a string is immutable and contained within a string pool (I
> realise that this is a c++ group, so apologies), in that in C++ the
> same string may refer to the same memory location (this is
> implementation defined) - assigning two literal strings that are the
> same to different pointers is conceptually similar to assigning the
> integer 5 to two different variables? Was there any reason apart from
> efficiency than choosing this meaning rather than 'each string literal
> creates a separate object'?
It is implementation-defined whether it makes the same string literals
shared (in fact, Visual C++ can do that, IIRC). With some compilers
(actually, linkers), this assertion
assert("abc" == "abc");
will be satisfied. But there is no requirement either way. If that's
what you're asking.
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Firstly, the above applies specifically to _built-in_ assignment
operator, not to every assignment in the program. Once we start talking
about user-defined assignment operators, all bets are off. There's no
relationship between lvalues/rvalues and user-defined assignment operators.
Secondly, once we restrict the consideration to built-in assignment
operator, the word "usually" becomes important. It is true that the
built-in assignment requires a [modifiable] lvalue on its LHS, this is
in no way a defining property of lvalue.
As for the RHS, it doesn't make much sense to say that an rvalue usually
appears there. Normally, just about anything can appear on the RHS. It
is correct to say that the RHS of an assignment is _used_ _as_ an
rvalue, but not correct o say that it _is_ an rvalue.
> An r-value might appear on the LHS of an assignment because of the
> following:
>
> * C++ allows a member function to be invoked on a temporary object
> * A function returning by value is an r-value
> * The assignment operator can be a member function of a UDT
You seem to be talking about the overladed assignment here. Once again,
the overloaded assignment has virtually no connection to the distinction
between lvalues and rvalues. It doesn't make much sense to get it
involved at all.
> However, I still don't understand how a string literal can be
> considered an l-value. The following excerpts hint at an answer:
>
> "String literal: because early C did not have 'const', and so old C
> functions that take 'char*' as argument could not be called with
> literal strings as actual arguments if string literals were considered
> rvalues. However, that may still change. It's just an old
> compatibility feature on its way out" - http://tiny.cc/yEr2A
>
> "char *x and char x[] are often used to refer to strings (null
> terminated arrays of characters), and they differ in their "lvalue-
> ness". " - http://tiny.cc/9JJBG
You seem to be using a bad source for your information. In both C and
C++ the term 'lvalue' is not tied to the "LHS of an assignment" in any
way. It is true that this is how this term was born, but in the end its
meaning was modified to something completely different. In a simplified
form, an lvalue is something that has a location in storage (address in
memory). In C the term 'lvalue' is synonymous with the term 'object'. In
C++ it is defined to also include functions and has some other
intricacies, but the general idea of "location in memory" remains the same.
If you want to express it through operators, an lvalue in C++ is
something you can apply the built-in unary operator '&' to (note: not
the LHS of the assignment operator, but the operand of address-of
operator). This is in no way an exact criterion, but it conveys the
general idea rather well.
> Wouldn't the following:
>
> void foo(char *);
>
> foo("Hello");
>
> be equivalent to:
>
> char * blah = "Hello";
> foo(blah);
>
> ? I don't see how this effects the l-valueness of a string literal
It doesn't. Pointers always point to lvalues in C and C++. The source
you are using doesn't make any sense.
> Also, is it a defining characteristic of an r-value that you can not
> take the address of an r-value (obviously this is true for most cases,
> I was wondering if it is, in fact, a defining characteristic, or
> whether there are some exceptions)
It works one way only: you can't take the address (i.e. use the built-in
unary '&') on an rvalue. However, just because you can't use the
built-in '&' on something doesn't mean it is an rvalue.
--
Best regards,
Andrey Tarasevich
>
> I'm thinking of an r-value more like a 'concept' rather than a an
> object, if that makes any sense. You can not change the 'concept' of
> the number 5, 5 will always be 5. This is re-inforced in C++ by only
> allowing to take a constant reference to temporaries:
>
> const int & rr = 3;
Err, the issue here is that you can't initialize a reference to a
modifiable int with a constant.
>
> Otherwise, you could do something like this:
>
> rr = 4;
>
> which would then change 3 to be equal to 4, which doesn't make sense!
Maybe not, but in Fortran you could do it. Accidentally, of course.
--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)
> This line is not an expression, but a declaration.
> >would be stored somewhere in memory
> »5« is an integer literal. What is being stored is its value.
> For example, the literal »5« and »05« have the same value.
> >I suppose that '5' can not be an l-value, because it isn't an
> >expression that refers to an object (it actually is the
> >object itself).
> »5« is not an object at all. »5« is an integer literal.
I think his point was that the value 5 must be stored somewhere
in memory. Probably as part of a load immediate machine
instruction, or some such, but it is still stored in memory.
I think the distinction he is missing is that C and C++ are
designed to support Harvard architectures, as well as von
Neumann architectures, and that conceptually, at least, "code"
(functions, etc.) isn't accessible memory. And that an rvalue
may never be in memory, accepssible or not; while the literal 5
will doubtlessly occur somewhere in code, the results of an
expression like "i + j" will typically never be in "memory", but
only in a register (which is, of course, also memory, but not
"accessible" memory, at least not from C/C++).
> >I suppose with that in mind you can't really modify the value
> >of 5, so with this definition '5' isn't an object.
> The value of literals is given by the C++ specification or
> sometimes by the C++ implementation, it can not be modified
> by a program.
But that has nothing to do with lvalue/rvalue. There are
lvalues that you cannot modify. (In C++, there are also rvalues
that you can modify, but that wasn't true in the original C.)
> >I'm thinking of an r-value more like a 'concept' rather than
> >a an object, if that makes any sense.
> Yes, it makes some sense. But thinking of an r-value as an
> object in the first place is so wrong that there actually is
> no need to deny it. (»I am thinking of a car more like a
> vehicle than a building.«)
An rvalue is a value, period. An lvalue is a reference to an
"object". Except that in C++, rvalues of class type are also
"objects", so the distinction becomes more or less arbitrary,
defined by the standard.
> "The type of a string literal is ??array of the appropriate number of
> c o n s t characters,?? so " B o h r " is
> of type c o n s t c h a r []"
> So thus a string literal is an l-value.
> Your responses say that a string literal is static and in a
> non-modifiable area of memory.
I didn't say that, and nor does the standard. Where the
compiler puts a string literal is not specified; all that the
standard says is that if you attempt to modify it, your program
has undefined behavior.
(A bit off the thread topic, but historically, the first edition
of K&R required that string literals be modifiable, and that
each instance of a string literal be a separate array. Very
much a misfeature, which was corrected by the C standard, but a
misfeature that was used in enough code that every compiler I
know has at least an option to support it, if it doesn't do so
by default.)
> Also, Bjarne writes "A string literal is statically allocated
> so that it is safe to return one from a function"
> Is this in any way similar to the Java implementation of
> Strings, where a string is immutable and contained within a
> string pool (I realise that this is a c++ group, so
> apologies), in that in C++ the same string may refer to the
> same memory location (this is implementation defined) -
> assigning two literal strings that are the same to different
> pointers is conceptually similar to assigning the integer 5 to
> two different variables?
Not really. First, in Java, even a string literal is a class
type (albeit a very special one), this is not the case in C++.
In Java, string literals are required to be pooled, and you can
force pooling of strings you create; any pooling in C++ is
implementation dependent.
The main motivation for string pooling, in Java, is
optimization; if you have two pooled strings, comparison for
equality can be shallow, and not deep. It's really only
interesting in very special cases. (The one case I needed it,
it wasn't for a string, but for a more complicated structure
known as a DN.)
> Was there any reason apart from efficiency than choosing this
> meaning rather than 'each string literal creates a separate
> object'?
I suspect that history plays an even greater role than
efficiency in the C++ definitions.
Isn't the distinction here that while an lvalue of class type is a
*reference* (in the general sense or the word) to an object, an rvalue
of class type *is* an object?
Thinking of it like this means that an lvalue can be converted to a
rvalue simply by "dereferencing" the lvalue.
--
Erik Wikström
> > On Dec 10, 12:39 am, r...@zedat.fu-berlin.de (Stefan Ram) wrote:
> >> Taras_96 <taras...@gmail.com> writes:
> >> >I'm thinking of an r-value more like a 'concept' rather than
> >> >a an object, if that makes any sense.
> >> Yes, it makes some sense. But thinking of an r-value as an
> >> object in the first place is so wrong that there actually is
> >> no need to deny it. (»I am thinking of a car more like a
> >> vehicle than a building.«)
> > An rvalue is a value, period. An lvalue is a reference to an
> > "object". Except that in C++, rvalues of class type are also
> > "objects", so the distinction becomes more or less arbitrary,
> > defined by the standard.
> Isn't the distinction here that while an lvalue of class type
> is a *reference* (in the general sense or the word) to an
> object, an rvalue of class type *is* an object?
Define the general sense of "reference". Both lvalues and
rvalues of class type have addresses, and the address can be
determined (e.g. by calling a member function, then using the
this pointer). For all practical intents and purposes, lvalues
and rvalues of class type are identical; the only difference is
that the standard says that certain operators require an lvalue,
and others require an rvalue.
> Thinking of it like this means that an lvalue can be converted
> to a rvalue simply by "dereferencing" the lvalue.
And a class type rvalue can be "converted" to an lvalue by
calling a member function. In neither case does anything change
with regards to the object.
> Define the general sense of "reference". Both lvalues and
> rvalues of class type have addresses, and the address can be
> determined (e.g. by calling a member function, then using the
> this pointer). For all practical intents and purposes, lvalues
> and rvalues of class type are identical; the only difference is
> that the standard says that certain operators require an lvalue,
> and others require an rvalue.
>
> > Thinking of it like this means that an lvalue can be converted
> > to a rvalue simply by "dereferencing" the lvalue.
>
> And a class type rvalue can be "converted" to an lvalue by
> calling a member function. In neither case does anything change
> with regards to the object.
>
Can you ever reference a class type rvalue without first using the
corresponding lvalue?
eg:
Foo foo;
Foo b = foo; // here the l-value foo is converted to an r-value, and
then used to initialise b
If this is correct, could you provide an example of a class type
rvalue being converted to an lvaue by calling a member function?
Taras
Sorry, that was meant to be 'your' plural.
>
> (A bit off the thread topic, but historically, the first edition
> of K&R required that string literals be modifiable, and that
> each instance of a string literal be a separate array. Very
> much a misfeature, which was corrected by the C standard,
Why is this a misfeature?
> > Define the general sense of "reference". Both lvalues and
> > rvalues of class type have addresses, and the address can be
> > determined (e.g. by calling a member function, then using
> > the this pointer). For all practical intents and purposes,
> > lvalues and rvalues of class type are identical; the only
> > difference is that the standard says that certain operators
> > require an lvalue, and others require an rvalue.
> > > Thinking of it like this means that an lvalue can be
> > > converted to a rvalue simply by "dereferencing" the
> > > lvalue.
> > And a class type rvalue can be "converted" to an lvalue by
> > calling a member function. In neither case does anything
> > change with regards to the object.
> Can you ever reference a class type rvalue without first using
> the corresponding lvalue?
That's a good question, at least if I understand it correctly.
Unless I've missed something (likely, since I haven't thought
about it that much), all possible uses of a class type rvalue do
require it to have an address, and act in all ways as if it were
an lvalue.
> eg:
> Foo foo;
> Foo b = foo; // here the l-value foo is converted to an r-value, and
> then used to initialise b
Not really. Here, the l-value foo is used to initialize the
reference parameter of the copy constructor.
A more interesting case would be:
Foo f() ;
Foo b = f() ;
Here, the rvalue is used to initialize the reference
parameter---in other words, it is behaving like an lvalue, or is
immediately converted to an lvalue (depending on how you want to
word it---but a reference is an lvalue, no matter how you look
at it).
> If this is correct, could you provide an example of a class
> type rvalue being converted to an lvaue by calling a member
> function?
The above, except that you don't even need to call a member
function. Otherwise, of course, the classical:
std::ostringstream().flush() << ...
std::ostringstream() is, of course, not an lvalue. But flush()
returns a reference to it, and a reference is an lvalue.
Are you saying that the fact that a literal isn't a constant,
and that when you read "abcd" in the code, it might actually be
"xyz", isn't a misfeature?
My attempt to sumarize conclusions made so-far:
All l-values are objects in memory, and therefore have an address.
R-values may or may not be values in memory, and may or may not have
an address.
R-values can be converted to lvalues thanks to the const T& binding.
L-values can be converted to r-values as needed.
Temporaries are normally R-values but because of the r-value to const T&
binding,
they can be converted to L-values.
Only lvalues can be on the left hand side of built in operators that modify
the right-hand side, or on wither side of operator++ and operator--.
That does not hold true for user-defined operator overloads.
L-values may or may not be modifiable.
R-values may or may not be modifiable, due to mutating member functions, or
the use of const_cast with the const T& binding.
Yeah, I'm going to say that this is a mess.
> news:8e5d755a-5c2f-493e...@m16g2000vbp.googlegroups.com...
> > I think I mostly understand l-values and r-values after
> > reading a few archived posts on this board:
> My attempt to sumarize conclusions made so-far:
> All l-values are objects in memory, and therefore have an
> address. R-values may or may not be values in memory, and may
> or may not have an address.
More precisely, r-values are objects in memory if and only if
they have class type. Of course, there is no way a conforming
program can tell if a non-class type rvalue is an object
(occupies memory) or not. And if you use the rvalue to
initialize a reference, it must occupy memory. So there's
really no reason to make the distinction---the standard could
just as easily say that all values are objects.
> R-values can be converted to lvalues thanks to the const T&
> binding.
Except that it's not considered a conversion, according to the
standard. Similarly, if you call a member function on an
r-value, the compiler has to give it an address, in order to
initialize the this pointer.
> L-values can be converted to r-values as needed.
> Temporaries are normally R-values but because of the r-value
> to const T& binding, they can be converted to L-values.
That's not quite the wording in the standard, but I think it
corresponds rather closely to the reality.
An important point here is that an object has a lifetime; a
temporary object normally has a lifetime until the end of the
full expression. (There's an interesting point here: does this
include the rvalue used to initialize a reference. In other
words, does the following code contain undefined behavior or
not:
#include <iostream>
void
f( int const& i )
{
pi = &i ;
}
void
g( int i )
{
std::cout << *pi << ',' << i << std::endl ;
}
int
main()
{
f( 1 ), g( 2 ) ;
return 0 ;
}
? If not, it's yet another example of a case where a non-class
rvalue behaves exactly like an object.)
> Only lvalues can be on the left hand side of built in
> operators that modify the right-hand side, or on wither side
> of operator++ and operator--. That does not hold true for
> user-defined operator overloads.
In general, only an lvalue can be "modified"; any operator which
modifies something requires an lvalue. What can happen,
especially with class types, is that the rvalue-ness is lost;
the lvalue- or rvalue-ness is not a characteristic of the
object, but of the expression.
> L-values may or may not be modifiable. R-values may or may
> not be modifiable, due to mutating member functions, or the
> use of const_cast with the const T& binding.
> Yeah, I'm going to say that this is a mess.
History. There was some discussion about dropping the
lvalue-rvalue distinction completely when the standard was being
formulated, but in the end, it was decided that built-in types
should follow the same rules as they do in C. And while I think
this does make things considerably more complicated, I can
understand that desire.
Anyway, there are a few simple rules which make it easy to
handle:
1. The distinction lvalue/rvalue only applies to expressions.
The standard specifies which expressions result in lvalues,
and it specifies which expressions require lvalues, for
which operands. And that's the only real meaning of
lvalue/rvalue---it's a more or less arbitrary distinction
based on the expression.
2. When considering expressions (and not just for
lvalue-/rvalue-ness), you have to first perform overload
resolution, and replace user defined operators with the
corresponding function call.
>
> A more interesting case would be:
>
> Foo f() ;
>
> Foo b = f() ;
>
> Here, the rvalue is used to initialize the reference
> parameter---in other words, it is behaving like an lvalue, or is
> immediately converted to an lvalue (depending on how you want to
> word it---but a reference is an lvalue, no matter how you look
> at it).
Good points ;). This is of course assuming that the c cstrctr takes a
reference, rather than a constant reference (otherwise there wouldn't
be need for the conversion to an l-value, as an r-value can be used to
initialise a constant reference).
>
> > If this is correct, could you provide an example of a class
> > type rvalue being converted to an lvaue by calling a member
> > function?
>
> The above, except that you don't even need to call a member
> function. Otherwise, of course, the classical:
>
> std::ostringstream().flush() << ...
>
> std::ostringstream() is, of course, not an lvalue. But flush()
> returns a reference to it, and a reference is an lvalue.
Also true :)
I don't see how this is safe.. wouldn't it be undefined? You're
effectively casting away the const in the line:
char array [] = "Hello";
Depends on what the definition of 'literal' is ;). If you take the
following definition: 'are invariants whose values are implied by
their representations.', then yes, it is a misfeature. For some reason
I thought (without giving it too much thought) that a 'literal' in
programming talk was a value which is hard coded into the source -
looks like it's a bit more than that.
Taras
No. This is a special syntax to initialise the array of char. What
book are you reading that doesn't explain arrays and their
initialisation? Consider:
int arr[] = { 1,2,3,4,5,6,7,8,9 };
What's 'arr' in your view? If it's an array, how many elements does it
have? What's the type of each element? If it isn't an array, what is it?
> > On Dec 13, 6:30 pm, Taras_96 <taras...@gmail.com> wrote:
> > A more interesting case would be:
> > Foo f() ;
> > Foo b = f() ;
> > Here, the rvalue is used to initialize the reference
> > parameter---in other words, it is behaving like an lvalue,
> > or is immediately converted to an lvalue (depending on how
> > you want to word it---but a reference is an lvalue, no
> > matter how you look at it).
> Good points ;). This is of course assuming that the c cstrctr
> takes a reference, rather than a constant reference (otherwise
> there wouldn't be need for the conversion to an l-value, as an
> r-value can be used to initialise a constant reference).
You missed the point. A reference (const or not) is an lvalue.
When you initialize a reference with a temporary (an rvalue),
you have effectively converted the rvalue to an lvalue.
> > Is officially undefined. Whereas:
> > Is perfectly safe.
> char array [] = "Hello";
No you're not. This is a special case---if you use a string
literal to initialize a char[] (or a char const[]), it is the
equivalent of "{ 'H', 'e', 'l', 'l', 'o', '\0' }". In other
words, it isn't a char const[] at all.
Ok, so char array [] = "Hello";
is the initialisation of a array of characters.. which is nothing to
do with string literals.
The original example given by Jason involved two fundamentally
different concepts. The first was initialising a char * to point to a
string literal, the second was the initialisation of a char array. For
some reason I thought they were the same with different syntax (in
hindsight they're obviously not).
Cheers
That's not entirely accurate. The array is initialised with a string
literal, as everybody can see. The array contains a C string (since
the elements are char, they are sequential, and terminated with a null
char). So, it's not "nothing", really.
> [..]
One of the important difference between C and C++ is
prefix ++ operator. The operator doesn't yield an l-value
in C but in C++ it yields a l-value.
I tried to overload the prefix ++ oprator for that class;
class MyInt
{
int m_i;
friend ostream & operator<< (ostream &, const MyInt &);
public:
MyInt(int i)
:
m_i(i)
{}
MyInt(const MyInt & r)
{
m_i = r.m_i;
}
MyInt & operator+= (const MyInt & anoth)
{
m_i += anoth.m_i;
return *this;
}
MyInt operator++ (int);
MyInt & MyInt::operator ++()
{
return *this += 1;
}
};
MyInt MyInt::operator++ ( int )
{
MyInt m_r( *this );
++*this;
return m_r;
}
MyInt operator+ (const MyInt & lefts, const MyInt & rights)
{
MyInt result = lefts;
result += rights;
return result;
// Or
// return MyInt( lefts ).operator+= ( rights );
}
int main()
{
MyInt myi( 5 );
// I am expecting error bu doesn't happen. Why?
myi++ = 10;
cout << myi;
}
It's nothing?? :). Surely it's an array of chars, as you said, the
array contains a C string, which is simply an array of chars
terminated with a null char.
You're assigning an integer to a temporary. The temporary will
go away as soon as the full expression is evaluated. It will be
destroyed with the value 10. Why would there be an error?
BTW, you stated that you "tried to overload the prefix ++ oprator"
while the class here has the *postfix* operator++ overloaded.
>
> cout << myi;
> > I tried to overload the prefix ++ oprator for that class;
> > class MyInt
> > {
[...]
> > MyInt operator++ (int);
>
> > MyInt & MyInt::operator ++()
> > {
> > return *this += 1;
> > }
> > };
> > MyInt MyInt::operator++ ( int )
> > {
> > MyInt m_r( *this );
> > ++*this;
> > return m_r;
> > }
> > // I am expecting error bu doesn't happen. Why?
> > myi++ = 10;
> You're assigning an integer to a temporary. The temporary
> will go away as soon as the full expression is evaluated. It
> will be destroyed with the value 10. Why would there be an
> error?
> BTW, you stated that you "tried to overload the prefix ++
> oprator" while the class here has the *postfix* operator++
> overloaded.
His class defined both, but his example effectively does use the
postfix operator. If myi were a built-in type, the expression
would be illegal, since assignment requires an lvalue for the
left operand, and the result of post-fix ++ is not an lvalue.
The difference here is that a user defined assignment operator
is a member function, and you can call a member function on an
rvalue. For most operators which require lvalues, the solution
is to declare the operator as a non-member, taking a non-const
reference---an rvalue cannot be used to initialize a non-const
reference. This isn't possible in the case of operator=,
however, since operator= must be a member.
Note that you don't really need any user defined operators at
all for this problem to manifest itself:
struct S { int i ; } ;
S f() ;
// ...
S s = { 42 } ;
f() = s ;
This won't compile in C, but is perfectly legal C++.