Are literals objects?

S

SasQ

Dnia Fri, 06 Apr 2007 11:37:48 -0400, Victor Bazarov napisa³(a):
We're going off on a tangent here, but objects of class types
have addresses even if they are r-values.

Ah, right. Huh, there's always some Xception :)
That's one of them. Thanks for mentioning it.
I am not sure, but maybe you're only talking built-in types.

The topic started from literal constants, which are for
built-in types only AFAIK. But evolved to r-values with
the flow of explanations. Feel free to discuss anything
related, I like occassions to learn something new ;)
 
G

Greg Comeau

SasQ said:
[..]
Literals are r-values and some expressions [yielding temporary
objects] are r-values too, so it's impossible to get their addresses.

We're going off on a tangent here, but objects of class types have
addresses even if they are r-values. Behold:

#include <iostream>
struct foo {
void bar() { std::cout << this << std::endl; }
};
int main() {
std::cout << "Temporary at " << foo().bar();
}

Isn't this trying to output the value of the pointer to member?
Which I don't think is defined (IOWs, the cout in main doesn't compile?)?
 
A

Alf P. Steinbach

* Greg Comeau:
Not sure what you mean. A string literal reflects a few things
including at some point an array of chars.

So, you should be able to take its address. ??

Yes, it's just an apparent contradiction, not a contradiction. A string
literal is an lvalue (by §5.1/2). I'm just noting that we're in
murky-land here, with some rather arbitrary and incomplete definitions.
 
G

Greg Comeau

* Greg Comeau:

Yes, it's just an apparent contradiction, not a contradiction. A string
literal is an lvalue (by §5.1/2). I'm just noting that we're in
murky-land here, with some rather arbitrary and incomplete definitions.

When all else fails, 43 is the answer, and no, 43's not an object. :)
 
V

Victor Bazarov

Greg said:
SasQ said:
[..]
Literals are r-values and some expressions [yielding temporary
objects] are r-values too, so it's impossible to get their
addresses.

We're going off on a tangent here, but objects of class types have
addresses even if they are r-values. Behold:

#include <iostream>
struct foo {
void bar() { std::cout << this << std::endl; }
};
int main() {
std::cout << "Temporary at " << foo().bar();
}

Isn't this trying to output the value of the pointer to member?

No, but I did type it too quickly and didn't test. It ought to be

int main() {
std::cout << "Temporary at "; foo().bar();
}
Which I don't think is defined (IOWs, the cout in main doesn't
compile?)?

Right.

V
 
J

James Kanze

* James Kanze:
[...]
On the other hand, the literal 7, if used in a program, might not even
exist in the machine code, because the compiler might generate machine
code that produces the same effect as using the number would have had.
Formally, that's true for objects as well.
Not really, no: with regard to what I intended to convey, you're
confusing existential quantifier with universal quantifier.

If you start using that kind of language, I will be confused.
All I meant to say is that the compiler can eliminate objects
(i.e. they won't exist in the machine code) if doing so has no
effect on the observable behavior of the program. Referring to
machine code isn't really an appropriate way to discuss what the
standard says, unless the machine in question is the abstract
machine referred to in §1.9/1 (in which case, I don't think you
can talk about machine code).
[snip]
According to the standard, string literals are objects, and
other literals aren't.
Chapter & verse for the "other literals arent't", please.

§5.1 and §3.10. According to §5.1/2, "A string literal is an
lvalue; all other literals are rvalues." And §3.10/1 says that
"An lvalue refers to an object or function. Some rvalue
expressions---those of (possibly cv-qualified) class or array
type---also refer to objects."
No, it doesn't define the notion of object clearly, at all.
First, the general definition of object lists the ways to create objects
as definitions, new-expressions and temporaries, which would exclude
string literals, which have static storage duration.
Second, it doesn't define "region" and is a bit inconsistent with regard
to how scattered in bits & pieces an object may be. Instead of simply
and clearly defining "region" as a contiguous sequence of bytes, and
adding suitable wording to the definition of object, it attempts to use
the usual meaning of "region" in general but hold the door open for
non-contiguous region. It's very, very, very silly & unclear.

I don't know. It seems clear to me. Clearer than most things
in the standard, anyway. (And it obviously has to hold the door
open for non-contiguous regions, since sub-objects are objects,
and a sub-object which contains a virtual base class will
typically not be contiguous.)
That seems to contradict earlier statement of string literals as object;
consider
int main() { &"Hello"; }
I'm just noting the apparent contradiction.
Regarding whether the code above /should/ compile, Comeau accepts it.

I fail to see where the problem is. String literals are
objects. They are also lvalues. They most definitly occupy a
region of storage. And you can certainly take their address.
Where is any contradiction, apparent or real?
Well, "creates a temporary": ITYM "any number of temporaries".

It must create a (one) temporary. Depending on the context, I
guess, it might create more.
But as I
understand it that rule will be changed in C++0x to allow temporaries of
class with no copy constructor to be passed to reference to const formal
argument. Which is generally nice.

Agreed, sort of... I do want it made clear that this means that
enclosing objects will also have their lifetime extended:

struct A {} ;
struct B { A a ; } ;

B f() ;

A const& refA = f().a ;

I forget the details right now (and it's late, and I don't feel
like looking them up), but in the current standard, it's either
unclear whether the lifetime of the temporary B object returned
from f is extended, or (which is what I recall), it is
positively forbidden to extend the lifetime of the B object. If
A does not have a copy constructor, and no copy is allowed, it
becomes necessary to extend the lifetime of the B object
returned by f().

(FWIW: I can't think of a realistic case where it would matter.
But a standard should be precise in such matters.)
 
A

Alf P. Steinbach

* James Kanze:
* James Kanze:
[...]
On the other hand, the literal 7, if used in a program, might not even
exist in the machine code, because the compiler might generate machine
code that produces the same effect as using the number would have had.
Formally, that's true for objects as well.
Not really, no: with regard to what I intended to convey, you're
confusing existential quantifier with universal quantifier.

If you start using that kind of language, I will be confused.
All I meant to say is that the compiler can eliminate objects
(i.e. they won't exist in the machine code) if doing so has no
effect on the observable behavior of the program. Referring to
machine code isn't really an appropriate way to discuss what the
standard says, unless the machine in question is the abstract
machine referred to in §1.9/1 (in which case, I don't think you
can talk about machine code).

Referring to machine code goes to rationale and practicality, under the
assumption that the standard wasn't created by devious aliens.

§5.1 and §3.10. According to §5.1/2, "A string literal is an
lvalue; all other literals are rvalues." And §3.10/1 says that
"An lvalue refers to an object or function. Some rvalue
expressions---those of (possibly cv-qualified) class or array
type---also refer to objects."

Consider

struct D { double v; D(): v() {} };

int main() { D().v; }

With the current C++03 rules, depending on how you read 'em, the
expression in main is possibly an lvalue, although the sub-expression
D() is an rvalue. g++ accepts an assignment to the full expression
D().v, msvc and comeau do not. With C++0x rules the expression will, as
I understand it, be an unassignable rvalue.

At least to my mind that changed designation does not alter in any way
the objectness of the expression. But what it does is to introduce a
conceptual inconsistency. You can do D() = D(), by virtue of the
automatically generated assignment operator, but not D().v = 0.

So here we have an (C++0x, and possibly also C++03) rvalue that clearly
refers to an object, namely a part of another object, yet isn't listed
in the cited paragraph as one that refers to an object.

Hence that list, being demonstrably incomplete, only demonstrates that
some rvalues refer to objects, not that all others don't.

Just so you don't misunderstand me, I'm not maintaining that e.g. 7 is
necessarily an object, just that formally, the standard is unclear, or
as I wrote earlier elsethread, that we're in murky-land here.

I don't know. It seems clear to me. Clearer than most things
in the standard, anyway. (And it obviously has to hold the door
open for non-contiguous regions, since sub-objects are objects,
and a sub-object which contains a virtual base class will
typically not be contiguous.)

I disagree that it's obvious. But my experience with this question is
that we could discuss it at extreme length, say, over two or three
weeks, and still not come to any agreed on conclusion. And the reason
for that is I think that the standard here is very subtly inconsistent
and intentionally vague, thus allowing for any viewpoint (of course,
since that is the question, we could discuss also that for...).

I fail to see where the problem is. String literals are
objects. They are also lvalues. They most definitly occupy a
region of storage. And you can certainly take their address.
Where is any contradiction, apparent or real?

No real contradiction, but there is the apparent one of taking the
address of a literal. It's handled by the standard by special-casing
string literals. OK, maybe it's a not-very-apparent possible
contradiction, or maybe I just wrote some nonsense (now that you mention
it, it seems most likely, although when Greg mentioned it, it still
seemed that that paragraph had some meaning -- I'm evolving here).

[snip]


Cheers,

- Alf
 
J

James Kanze

* James Kanze:

[...]
Referring to machine code goes to rationale and practicality, under the
assumption that the standard wasn't created by devious aliens.

The problem is that machine code is a result of optimization,
and doesn't necessarily reflect the wording of the standard.
And I'm not sure how much it helps with what is, at least in
part, a problem of vocabulary. Consider something like:

for ( int i = 0 ; i < 3 ; ++ i ) {
doSomething() ;
}

According to the standard, i is an object. If the compiler puts
i in a register (most will, if optimization is invoked), does it
cease to be an object? What if the compiler unrolls the loop,
making three copies, and eliminates i entirely?
struct D { double v; D(): v() {} };
int main() { D().v; }
With the current C++03 rules, depending on how you read 'em, the
expression in main is possibly an lvalue, although the sub-expression
D() is an rvalue. g++ accepts an assignment to the full expression
D().v, msvc and comeau do not. With C++0x rules the expression will, as
I understand it, be an unassignable rvalue.

OK. I was thinking of complete objects. C++ inherits some of
the vagueness of C with regards to subobjects. Eliminate the
constructor, and replace D() with a function which returns the
resulting struct, and you have a legal C program, with exactly
the same problem. IIRC (it's been some time since I looked at
the issue), there were some really hairy problems when the
struct contained an array---what does the implicit array to
pointer conversion mean on a non-lvalue?

C++ actually has it a little easier here, since in C++, "some
rvalue expressions [...] also refer to objects".
At least to my mind that changed designation does not alter in any way
the objectness of the expression.

I think you have a point. If the D which contains the v is an
object, it's hard to say that the v isn't, even if it is an
rvalue.

Note that this has significant implications with regards to
extending lifetime when binding a temporary to a const
reference. Consider something like:

struct A { /* user defined ctors and dtor */ } ;
struct B { A a ; /* user defined ctors and dtor */ } ;
A const& rd = B().a ;

(The "user defined ctors and dtor" are only to ensure that the
program can tell if the lifetime has been extended or not.)

We know that the lifetime of the A being bound is extended
to correspond to that of the reference, but what about the
lifetime of the D which contains it? I don't have my copy of
the original version of the standard here to verify, but I seem
to recall that the standard could very well have been
interpreted as forbidding the extension of lifetime of the D,
and that this was the reason why the copy was allowed. The
draft I do have access to here (N 2009, not the very latest, but
relatively recent) seems to also ban the extension of the
lifetime, and also ban copying the element.

Or at least, it could be interpreted that way. The other
possible interpretation would be that it requires that the
lifetime of B be extended as well. Which makes more sense, but
then:

struct A { /* user defined ctors and dtor */ } ;
struct B { A a ; int b ; /* user defined ctors and dtor */ } ;

A const& r1 = B().a ; // extends lifetime of B
int const& r2 = B().b ; // doesn't extend lifetime of B
But what it does is to introduce a
conceptual inconsistency. You can do D() = D(), by virtue of the
automatically generated assignment operator, but not D().v = 0.

Yup. But that's generally consistent with the idea that you can
call member functions on a temporary with class type, but that
you can't do anything else which would make them "object-like".
(In sum, it's consistent with the other inconsistencies in the
standard.)

I was very active in standardization when this was originally
being discussed. I know that at one point, I raised the
possibility of stating that everything was an object (including
the integral literal 7), merging the distinction lvalue/rvalue
with that of reference/non-reference (with the name of a
variable having type "reference to T", when used in an
expression), and handling everything within the type system.
This would have meant that an expression like "&7" would be
legal (with the lifetime of the object 7 that of a normal
temporary).

I think that the resulting language would have been cleaner and
easier to understand. However, the idea represented too great a
break with C and the traditions, and didn't bring that much
practical advantage.

[...]
Just so you don't misunderstand me, I'm not maintaining that e.g. 7 is
necessarily an object, just that formally, the standard is unclear, or
as I wrote earlier elsethread, that we're in murky-land here.

OK. I thought that you were doubting that the standard said (or
intended to say) that integral literals weren't objects. I
agree that when we leave complete objects, and move into the
realm of sub-objects, much of the clarity is lost.
I disagree that it's obvious.

Well, I think I was being a bit careless about the distinction
complete object/sub-object. It's pretty clear that if there are
several sub-objects (base classes) in a class which all
virtually derive from the same class, they cannot all be
contiguous. But as you correctly point out, there are enough
other ambiguities with regards to the "object-ness" of
sub-objects that the standard probably needs to treat the case
explicity. It think that there would be no problem in saying
that complete objects must occupy a contiguous extent in memory,
and that sub-objects occupy some or all of the bytes of the
complete object of which they are a part.
But my experience with this question is
that we could discuss it at extreme length, say, over two or three
weeks, and still not come to any agreed on conclusion.

Except, maybe, that the wording could be, or needs to be,
improved:).
And the reason
for that is I think that the standard here is very subtly inconsistent
and intentionally vague, thus allowing for any viewpoint (of course,
since that is the question, we could discuss also that for...).

I don't think that the vagueness is intentional in this case. I
think that it's more a case of certain concepts being taken over
from C and adapted, without realizing that the concept was
already broken in C (and that it definitly didn't scale). To
date, regretfully, the attitude has not been to say that the
concept is broken, and to find a new one, but to patch the leaks
as they appear. The result is, well, hardly elegant.
No real contradiction, but there is the apparent one of taking the
address of a literal.

OK. I can see that this would be a problem for a beginner, but
the language never considers the question of literal or not when
defining the possible uses of an operator. It doesn't really
bother me. At least, no more than the fact that "hello"[3], or
even 3["hello"] are legal expressions.
It's handled by the standard by special-casing
string literals.

I'm not sure I'd consider it special-casing. Like a lot of
other expressions, literals can be lvalues or not, depending on
the type.
OK, maybe it's a not-very-apparent possible
contradiction, or maybe I just wrote some nonsense (now that you mention
it, it seems most likely, although when Greg mentioned it, it still
seemed that that paragraph had some meaning -- I'm evolving here).

Me too. The funny thing is that my evolution is bringing me
back to a position I held 12 or more years ago, and had more or
less forgotten.

--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top