Microsoft lvalue definition:
An lvalue refers to an object that persists beyond a single
expression.
A second definition:
The expression E belongs to the lvalue category if and only if E
refers to an entity that ALREADY has had an identity (address, name or
alias) that makes it accessible outside of E.
I wrote the following code:
class A{};
const A& f1()
{
return A();
}
const int& f2()
{
return 1;
}
int main() {
cout<<&f1()<<endl; // this prints everytime "0".
cout<<&f2()<<endl; // this prints everytime "0".
return 0;
}
Why f1() and f2() are lvalue expressions?
Why an address of lvalue reference to rvalue is zero?
Why are both definitions equivalent?
Why f1() and f2() are lvalue expressions?
Because each are a function call to a function that returns an lvalue reference.
Standard draft: [expr.call]
11 A function call is an lvalue if the result type is an lvalue reference type or ...
Why the & character after the type name makes it an lvalue reference?
Standard draft: [dcl.ref]
1 In a declaration T D where D has either of the forms
& attribute-specifier-seqopt D1
&& attribute-specifier-seqopt D1
and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is “derived-declarator-type-list reference to T” ...
2 A reference type that is declared using & is called an lvalue reference ...
Why an addres of lvalue reference to rvalue is zero?
The behaviour is undefined.
Standard draft: [expr.unary.op]
3 The result of the unary & operator is ... the result has type “pointer to T” and is a prvalue that is the address of the designated object
There is no designated object, and the standard doesn't define the behaviour of the addressof operator in that case.
Standard draft: [defns.undefined]
behavior for which this document imposes no requirements
[ Note: Undefined behavior may be expected when this document omits any explicit definition of behavior ...
Why are both definitions equivalent?
They aren't necessarily equivalent. One or both of them may be incorrect. Both appear to be descriptions of lvalue expressions, rather than definitions.
The normative definition is in the C++ standard document.
What is the definition of lvalue by the standard?
Standard draft: [basic.lval]
(1.1) A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.
...
(1.3) An xvalue is a glvalue that denotes an object or bit-field whose resources can be reused (usually because it is near the end of its lifetime).
[ Example: Certain kinds of expressions involving rvalue references ([dcl.ref]) yield xvalues, such as a call to a function whose return type is an rvalue reference or a cast to an rvalue reference type.
— end example
]
(1.4) An lvalue is a glvalue that is not an xvalue.
The [expr] section defines each possible expression in the language, and if the expression is an lvalue, then that is stated. "is an lvalue" occurs 37 times, but this simple search is not necessarily exhaustive.
Declaring a function with lvalue reference return type means that that function call is an lvalue expression (nothing more and nothing less).
The pages you linked to are both wrong in equating lvalue expressions with objects that "already exist" or "persist" or whatever. In your code is an example of an lvalue expression that refers to an object that only existed during the function call.
Using the result of the function call causes undefined behaviour because the behaviour of lvalue expressions is only defined for when they actually refer to an object. (Plus a few cases for referring to a potential object under construction or destruction, but that doesn't apply here, since the object's associated storage is already released by the time the calling code uses the result of the expression).
Undefined behaviour means anything can happen, including (but not limited to) outputting a zero.
You end up returning a hanging reference because the returned reference has nothing to reference because A() is destructed at the end of the method. What you have is undefined behavior. The 0 is a placeholder for the fact that it is not referencing any memory location. The f2() function returns another temporary variable as reference. To be absolutely clear the memory location they return is 0 because the memory location they reference does not exit any longer.
Hope this helps.
Related
Look at this expression:
T t;
T& ref = t;
The ref expression is an lvalue or an rvalue? I believe it's an rvalue because ref does not "designates a function or an object":
"An lvalue (so called, historically, because lvalues could appear on
the left-hand side of an assignment expression) designates a function
or an object."
[open-std draft n3092]
According to cppreference, a reference is not a object.
"The following entities are not objects: value, reference [...]"
I'm in doubt, because ref is in the left side of =.
I'm in doubt, because ref is in the left side of =.
ref is a name and therefore an lvalue.
I believe it's an rvalue because ref does not "designates a function or an object"
It designates the same object as t does.
A reference does designate a function or object! It is literally written like that in the standard! See in [expr.type]/1:
If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis.
The expression designates the object or function denoted by the reference,[...]
With code like
#include <stdio.h>
struct P2d {
double x, y;
P2d(double x, double y) : x(x), y(y) {}
~P2d() { printf("Destructor called\n"); }
};
P2d center() {
return P2d(10, 10);
}
int main(int argc, const char *argv[]) {
const double& x = center().x;
printf("x = %.18g\n", x);
return 0;
}
g++ (version 5.2.0) will destroy the P2d temporary instance before entering the printf in main, but the value will be preserved anyway (i.e. instead of binding x to the actual member of the temporary P2d instance it will create another temporary double to copy the value of the member).
clang++ (IMO correctly) instead extends the lifetime of the temporary P2d instance to the lifetime of the x reference and the destructor will be therefore called after the printf in main.
If instead of using plain double as type for the x and y members you make a class (e.g. Double) then both compilers agree and they extend the lifetime of the temporary P2d object to past the printf.
Is this a bug in g++ or something allowed by the standard?
This is covered by CWG 1651:
The resolution of issues 616 and 1213, making the result of a member
access or subscript expression applied to a prvalue an xvalue, means
that binding a reference to such a subobject of a temporary does not
extend the temporary's lifetime. 12.2 [class.temporary] should be
revised to ensure that it does.
The status quo is that only prvalues are treated as referring to temporaries - thus [class.temporary]/5 ("The second context is when a reference is bound to a temporary.") is not considered applicable. Clang and GCC have not actually implemented issue 616's resolution, though. center().x is treated as a prvalue by both. My best guess:
GCC simply didn't react to any DRs yet, at all. It doesn't extend lifetime when using scalar subobjects, because those are not covered by [dcl.init.ref]/(5.2.1.1)†. So the complete temporary object doesn't need to live on (see aschelper's answer), and it doesn't, because the reference doesn't bind directly. If the subobject is of class or array type, the reference binds directly, and GCC extends the temporary's lifetime. This has been noted in DR 60297.
Clang recognizes member access and implemented the "new" lifetime extension rules already - it even handles casts. Technically speaking, this is not consistent with the way it handles value categories. However, it is more sensible and will be the correct behavior once the aforementioned DR is resolved.
I'd therefore say that GCC is correct by current wording, but current wording is defective and vague, and Clang already implemented the pending resolution to DR 1651, which is N3918. This paper covers the example very clearly:
If E1 is a temporary expression and E2 does not designate a
bit-field, then E1.E2 is a temporary expression.
center() is a temporary expression as per the paper's wording for [expr.call]/11. Thus its modified wording in the aforementioned [class.temporary] /5 applies:
The second context is when a reference does not bind directly (8.5.3
dcl.init.ref) or is initialized with a temporary expression (clause 5). The corresponding temporary
object (if any) persists for the lifetime of the reference except: [...inapplicable exceptions...]
Voilà, we have lifetime extension. Note that "the corresponding temporary object" is not clear enough, one of the reasons for the proposal's deferment; it will assuredly be adopted once it gets revised.
†
is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or […]
Indeed, GCC respects this fully and will extend lifetime if the subobject has array type.
I would argue for a bug in g++, because, quoting draft N3242, §12.2/5:
The second context is when a reference is bound to a temporary. The temporary to which the reference is
bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
So its lifetime must be extended, except when:
A temporary bound to a reference member in a constructor’s ctor-initializer [..]
A temporary bound to a reference parameter in a function call [..]
The lifetime of a temporary bound to the returned value in a function return statement [..]
A temporary bound to a reference in a new-initializer [..]
Our case doesn't fit any of these exceptions, thus it must follow the rule. I'd say g++ is wrong here.
Then, regarding the quote aschepler brought up from the same draft §8.5.3/5 (emphasis mine):
A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows:
If the reference is an lvalue reference and the initializer expression
a. is an lvalue (but is not a bit-field) and "cv1 T1" is reference-compatible with "cv2 T2", or
b. has a class type ...
then ...
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.
a. If the initializer expression
i. is an xvalue, class prvalue, array prvalue or function lvalue and "cv1 T1" is reference-compatible with "cv2 T2", or
ii. has a class type ...
then the reference is bound to the value of the initializer expression in the first case....
b. Otherwise, a temporary of type "cv1 T1" is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (8.5). The reference is then bound to the temporary.
Looking at what an xvalue is, this time quoting http://en.cppreference.com/w/cpp/language/value_category ...
An xvalue ("expiring value") expression is [..]
a.m, the member of object expression, where a is an rvalue and m is a non-static data member of non-reference type;
... the expression center().x should be an xvalue, thus case 2a from §8.5.3/5 applies (and not the copy). I'll stay with my suggestion: g++ is wrong.
Just read Columbo's answer.
This is a gcc bug. The relevant rule is in [class.temporary]:
There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. [...]
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists
for the lifetime of the reference except:
— A temporary object bound to a reference parameter in a function call (5.2.2) persists until the completion
of the full-expression containing the call.
— The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not
extended; the temporary is destroyed at the end of the full-expression in the return statement.
— A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the
full-expression containing the new-initializer.
We're binding a reference to a subobject of a temporary, so the temporary should persist for the lifetime of the reference. None of those three exceptions to this rule apply here.
The C++11 and C++14 standard (and working draft, respectively) say in §3.10.1:
A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [Example: The result of calling a function
whose return type is not a reference is a prvalue. The value of a literal such as 12, 7.3e5, or true is
also a prvalue. —end example ]
and
An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment
expression) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated
with an object.
Which leads me to the question: How can an expression be "a value not associated with an object"?
I was under the impression, that it is the purpose of expressions to return objects or void (which I do not expect to be a value either).
Is there some simple and common example for such expressions?
Edit 1
To further complicate things, consider the following:
int const& x = 3;
int&& y = 4;
In context of §8.3.2.5, which contains the most interesting snippet:
[...] A reference shall be initialized to refer to a valid object or
function [...]
Which is reinforced by §8.5.3.1:
A variable declared to be a T& or T&&, that is, “reference to type T” (8.3.2), shall be initialized by an object,
or function, of type T or by an object that can be converted into a T. [...]
[intro.object]:
The constructs in a C++ program create, destroy, refer to, access, and manipulate objects. An object is a region of storage. [ Note: A function is not an object, regardless of whether or not it occupies storage in the way that objects do. —end note ] An object is created by a definition (3.1), by a new-expression (5.3.4) or by the implementation (12.2) when needed.
So "a value not associated with an object" is something created not by definition or with new-expression, which also means that it doesn't have corresponding region of storage, like for example a literal.
Edit: Except string literals (see comments)
Examples for such values are all non-array, non-class non-temporary prvalues (a temporary prvalue corresponds to a temporary object). Examples include 2.0 and 1. Counterexamples include "hello" (which is an array), std::string("haha") (which is a class object) or the float prvalue temporary initialized from 2 that is bound to the reference in (const float&){2} (the reference itself is an lvalue!). I think that this simple rule covers the rules accurately.
A C++ Standard's footnote on the lvalue to rvalue conversion says (a little bit outdated, because it was not amended to mention array types)
In C ++ class prvalues can have cv-qualified types (because they are objects). This differs from ISO C, in which non-lvalues never have cv-qualified types.
So the deeper reason that decltype((const int)0) still is type int is that it does not refer to an object. So because there is no object, there is nothing to make const, and consequently the expression will never be const either.
This quote is not as precisely worded as it could be:
An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment expression) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated with an object.
An rvalue is an expression , so it cannot be an object (temporary or otherwise). The intent of the section of this quote talking about temporary objects is to say that value resulting from evaluating the rvalue is a temporary object, and so on.
This is a common shortcut, e.g. with int x; we would casually say "x is in int" , when in fact x is an identifier; and the expression x has type int and designates an int.
Anyway, it divides possible rvalues up into three categories:
xvalue
temporary object
value not associated with an object
The definition of temporary object includes being an object of class type, so it seems to me that "value not associated with an object" should be any non-xvalue of non-class type. For example 1 + 1.
I'm trying to understand the C++11 concepts.
The standard draft which I have says:
An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its
resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving
rvalue references (8.3.2). [ Example: The result of calling a function whose return type is an rvalue
reference is an xvalue. —end example ]
OK, so what exactly are the "certain kinds of expressions" that produce xvalues? This part of the spec does not detail a list of these expressions.
I understand lvalue and prvalue (at least I think, I understand).
There is a helpful non-normative note in the introduction to §5 (C++11 §5[expr]/6):
[ Note: An expression is an xvalue if it is:
the result of calling a function, whether implicitly or explicitly, whose return type is an rvalue reference to object type,
a cast to an rvalue reference to object type,
a class member access expression designating a non-static data member of non-reference type in which the object expression is an xvalue, or
a .* pointer-to-member expression in which the first operand is an xvalue and the second operand is a pointer to data member.
In general, the effect of this rule is that named rvalue references are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not. —end note ]
Searching through the rest of §5, this list appears exhaustive. The list is followed by an example:
struct A {
int m;
};
A&& operator+(A, A);
A&& f();
A a;
A&& ar = static_cast<A&&>(a);
The expressions f(), f().m, static_cast<A&&>(a), and a + a are xvalues. The expression ar is an lvalue.
There are two common ways to get an xvalue expression:
Use std::move to move an object. std::move performs a static_cast to an rvalue reference type and returns the rvalue reference.
Use std::forward to forward an rvalue. std::forward is typically used in a function template to enable perfect forwarding of a function argument.
If the argument provided to the function template was an rvalue, the parameter type will be an rvalue reference, which is an lvalue. In this case, std::forward performs a static_cast to an rvalue reference type and returns the rvalue reference.
(Note: If the argument provided to the function template was an lvalue, the parameter type will be an lvalue reference and std::forward will return an lvalue reference.)
Clause 5, which describes the syntax of valid expressions, lists for each expression syntax the conditions in which the expression is an lvalue, an xvalue, or a prvalue. The complete list of possible xvalues from clause 5 is:
5.2.2 paragraph 10: A function call is ... an xvalue if the result type is an rvalue reference to object type.
(In the technical language of the Standard, "object type" doesn't mean the same as "class type". "Object type" includes fundamental types, pointers, and arrays, and excludes only function types. An rvalue reference to function type is always treated as an lvalue, not xvalue.)
The most notable functions which return an rvalue reference are of course std::move and sometimes std::forward.
5.2.5 paragraph 4: If E2 is a non-static data member ... if E1 is an xvalue, then E1.E2 is an xvalue
(On the other hand, a data member lookup E1->E2 is always an lvalue.)
Similarly, if E1 is an xvalue, then the data member lookup E1.*E2 is an xvalue:
5.5 paragraph 6: The result of a .* expression whose second operand is a pointer to a data member is of the same value category (3.10) as its first operand.
For the various types of casts:
dynamic_cast<Type>(expr): 5.2.7 paragraph 2
static_cast<Type>(expr): 5.2.9 paragraph 1
reinterpret_cast<Type>(expr): 5.2.10 paragraph 1
const_cast<Type>(expr): 5.2.11 paragraph 1
(Type) expr: 5.4 paragraph 1
the expression is an xvalue if and only if Type is an rvalue reference to object type. The same is also true for Type(expr), since
5.2.3 paragraph 1: If the expression list [in parentheses following a type name] is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4).
(On the other hand, Type{expr} is always a prvalue.)
Section 5.16 on the conditional operator ends up saying that A ? B : C can sometimes be an xvalue if B and/or C is an xvalue. But the complete rules are difficult to summarize.
If an expression ends up calling a user-defined overloaded operator function, then section 5.2.2 applies to that expression, not the one that describes the built-in operator's behavior. (See the expression a + a in the example #James posted.)
As you mentioned,
An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example).
Focus: object, be moved, end lifetime
Here is an example:
void move_test(){
std::string s = "I'm here!";
std::string m = std::move(s); // move from <s> to <m>
// s is now in an undefined, but valid state;
std::cout << "s=" << s << "; &s=" << &s << std::endl;
}
By moving, we can convert a named value (an lvalue,here is s) to being an rvalue(that is std::move(s)). More specifically, since it can’t be a prvalue (it has a name! In other words, it has an identity), it ends up being an xvalue.
xvalue always serves C++ move semantics.
What I gather from what I've read is that calling something an xvalue is a fancy way of saying:
An xvalue is just an rvalue whose storage may have been deallocated, so using it means you have to verify its existence by yourself.
Generally, it's one or more levels of indirection away from an actual rvalue.
By contrast, an rvalue is guaranteed to have its storage space existing as long as it is in scope.
I may be wrong but this is what I've understood.
Given:
int main() {
int x = 0;
int y = x; // <---
}
Could someone please tell me which clause of the standard (2003 preferred) mandates the conversion of the expression x from lvalue to rvalue in the initialisation of the object y?
(Or, if I'm mistaken and no such conversion takes place, then I'd like to learn that too!)
I find it easier (if maybe not 100% precise) to think of lvalue-s as real objects and rvalue-s as the value stored in the object. The expression x is an lvalue expression that refers to the object x defined in the first line, but when used as the right hand side of an assignment to a type that is not a user defined type the actual value is read, and that is where the conversion from lvalue to rvalue is performed: reading the contents of the object.
As to the specific clause in the standard that dictates that conversion... well, the closest that I can think is 4.1 [conv.lvalue]/2 (Lvalue to Rvalue conversion):
The value contained in the object indicated by the lvalue is the rvalue result.
The requirement that the right hand side of the assignment is an rvalue is either implicit or missing from 5.17 [expr.ass], but that is the case or else the following expression would be an error since the rhs is an rvalue and there is no rvalue-to-lvalue conversion:
int x = 5;
EDIT: For initialization, 8.5 [dcl.init]/14, last bullet (which refers to fundamental types) states (emphasis mine):
Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression. [...]
That value there means that the lvalue expression in your example is read (i.e. converted to an rvalue). At any rate the previous paragraph that referred to assignment could be applied here: if initialization required an lvalue rather than an rvalue, the expression int i = 0; would be ill-formed.
I do believe that this is intuitive to some degree (what others already said - the value is needed, so there is an obvious need to convert the object designator to the value contained therein). The best I could come up with, by 4p3:
An expression e can be implicitly converted to a type T if and only if the declaration "T t=e;" is well-formed, for some invented temporary variable t (8.5). The effect of the implicit conversion is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion. The result is an lvalue if T is a reference type (8.3.2), and an rvalue otherwise. The expression e is used as an lvalue if and only if the initialization uses it as an lvalue.
Note the "if and only if" at the end - the initializer therefor is used as an rvalue, because the initialization uses it as an rvalue (result of the conversion). So by 3.10p7
Whenever an lvalue appears in a context where an rvalue is expected, the lvalue is converted to an rvalue; see 4.1, 4.2, and 4.3.
EDIT: The paragraph for entering 4p3 can be found at 8.5p16, last bullet:
Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression.
Also note the comments below.
Is this what you're looking for:
§3.10/7
Whenever an lvalue appears in a context where an rvalue is expected, the lvalue is converted to an rvalue; see 4.1, 4.2, and 4.3.
And I think when you write int y = x, it basically copies the value contained in the object x which is a lvalue, but the value itself is an rvalue, hence the context expects an rvalue.
§4.1/2 says,
The value contained in the object indicated by the lvalue is the rvalue result.
Maybe these two quotations clarify your doubt. Correct me if my understanding is wrong. I would like to learn new things.
#Tomalak's comment:
My problem with this is that int& y = x; is valid, so in this case of course x may not be an rvalue. I don't know how irrelevant the difference in my example makes that, though
Well int &y = x does NOT copy the value. It just creates an alias of the object itself. But as I previously said int y = x, basically copies the value which is an rvalue. Hence, the context expects an rvalue, as a copying is being done here.
The initializer has the follwing grammar:
initializer:
= initializer-clause
( expression-list )
initializer-clause:
assignment-expression
{ initializer-list ,opt }
{ }
In your example, x is an assignment-expression which follows this chain of grammar productions:
conditional-expression ==>
logical-or-expression ==>
logical-and-expression ==>
inclusive-or-expression ==>
exclusive-or-expression ==>
and-expression ==>
equality-expression ==>
relational-expression ==>
shift-expression ==>
additive-expression ==>
multiplicative-expression ==>
pm-expression ==>
cast-expression ==>
unary-expression ==>
postfix-expression ==>
primary-expression ==>
id-expression ==>
unqualified-id ==>
identifier
And an identifier "is an lvalue if the entity is a function or variable" (5.1/4 "Primary expressions").
So in your example, the expression to the right of the = is an expression that happens to be an lvalue. It could be an rvalue of course, but it doesn't have to be. And there is no mandated lvalue-to-rvalue conversion.
I'm not sure what the value in knowing this is, though.
3.10 Lvalues and rvalues
1 Every expression is either an lvalue
or an rvalue.
2 An lvalue refers to an object or
function. Some rvalue
expressions—those of class or
cvqualified class type—also refer to
objects.47)
3 [Note: some builtin operators and
function calls yield lvalues.
[Example: if E is an expression of
pointer type, then *E is an lvalue
expression referring to the object or
function to which E points. As another
example, the function int& f(); yields
an lvalue, so the call f() is an
lvalue expression. ]
[Note: some builin operators expect lvalue operands. [Example: builtin
assignment operators all expect their
left hand operands to be lvalues. ]
Other builtin operators yield rvalues,
and some expect them. [Example: the
unary and binary + operators expect
rvalue arguments and yield rvalue
results. ] The discussion of each
builtin operator in clause 5 indicates
whether it expects lvalue operands and
whether it yieldsan lvalue. ]
5 The result of calling a function
that does not return a reference is an
rvalue. User defined operators are
functions, and whether such operators
expect or yield lvalues is determined
by their parameter and return types.
6 An expression which holds a
temporary object resulting from a cast
to a nonreference type is an rvalue
(this includes the explicit creation
of an object using functional notation
(5.2.3)).
7 Whenever an lvalue appears in a context where an rvalue is expected,
the lvalue is converted to an rvalue;
see 4.1, 4.2, and 4.3.
8 The discussion of reference
initialization in 8.5.3 and of
temporaries in 12.2 indicates the
behavior of lvalues and rvalues in
other significant contexts.
9 Class rvalues can have cvqualified
types; nonclass rvalues always have
cvunqualified types. Rvalues shall
always have complete types or the void
type; in addition to these types,
lvalues can also have incomplete
types.
10 An lvalue for an object is
necessary in order to modify the
object except that an rvalue of class
type can also be used to modify its
referent under certain circumstances.
[Example: a member function called for
an object (9.3) can modify the object.
]
11 Functions cannot be modified, but
pointers to functions can be
modifiable.
12 A pointer to an incomplete type can
be modifiable. At some point in the
program when the pointed to type is
complete, the object at which the
pointer points can also be modified.
13 The referent of a constqualified
expression shall not be modified
(through that expression), except that
if it is of class type and has a
mutable component, that component can
be modified (7.1.5.1).
14 If an expression can be used to
modify the object to which it refers,
the expression is called modifiable. A
program that attempts to modify an
object through a nonmodifiable lvalue
or rvalue expression is illformed.
15 If a program attempts to access the
stored value of an object through an
lvalue of other than one of the
following types the behavior is
undefined48): — the dynamic type of
the object, — a cvqualified version of
the dynamic type of the object, — a
type that is the signed or unsigned
type corresponding to the dynamic type
of the object, — a type that is the
signed or unsigned type corresponding
to a cvqualified version of the
dynamic type of the object, — an
aggregate or union type that includes
one of the aforementioned types among
its members (including, recursively, a
member of a subaggregate or contained
union), — a type that is a (possibly
cvqualified) base class type of the
dynamic type of the object, — a char
or unsigned char type.