What happen with this 'B()'?

V

Vols

class A{
public:
int x;
};
class B : public A{
public:
int y;
};

void foo()
{
A &a1 = B();
A *a2 = &B();
}

what happen with those 'B()'. I've never seen this before. Where can I
get the reference for this? Thanks

Vol
 
J

Jim Langston

Vols said:
class A{
public:
int x;
};
class B : public A{
public:
int y;
};

void foo()
{
A &a1 = B();
A *a2 = &B();

This line is dangerous and your compiler should issue a warning. You are
setting a pointer to a temporary object. After this line is done, a2 will
point to an object that's been destructed causing undefined behavior if you
attempt to dereference a2.

I'm not sure about the previous line, I know that there is something about
references pointing to a temporary that will extend the lfietime of the
temporary, but I'm not sure if this case fits the bill or not.

B() simply creates a temporary instance of class B default initialized.
 
J

James Kanze

class A{
public:
int x;
};
class B : public A{
public:
int y;
};
void foo()
{
A &a1 = B();
A *a2 = &B();
}

what happen with those 'B()'. I've never seen this before.

Me neither. Both lines in foo() are illegal in C++, and require
a diagnostic. (Much to my surprise, g++ only generates a
warning on the second, and Sun CC doesn't complain about
either! Which is IMHO simply not acceptable. Particularly the
second, which has never been legal C++.)
 
B

Bernd Strieder

Hello,

James said:
Me neither. Both lines in foo() are illegal in C++, and require
a diagnostic. (Much to my surprise, g++ only generates a
warning on the second, and Sun CC doesn't complain about
either! Which is IMHO simply not acceptable. Particularly the
second, which has never been legal C++.)

Can't follow that. All g++-3.3.6, 4.2.1, and 4.3.0 make the first line
in foo an error and the second a warning without any special settings.
The first line is the one that assigns reference to a temporary to a
non-const reference type, which cannot work. The second line seems to
have the same problem with const pointer and non-const pointer at
first.

liftim1.cc: In function ?void foo()?:
liftim1.cc:12: error: invalid initialization of non-const reference of
type ?A&? from a temporary of type ?B?
liftim1.cc:13: warning: taking address of temporary

A temporary object is not a const object by definition if there is no
const in the declaration. The standard mandates that assigning a
temorary to a non-const reference is not allowed, but this decision is
kind of deliberate, because it would produce hard to detect errors most
of the time. But taking the pointer is not the same es taking a
reference. Using that pointer could be problematic, especially
dereferencing, but the heuristical warning is the best one can hope for
when taking the address alone.

Bernd Strieder
 
J

James Kanze

Bernd said:
James Kanze wrote:
Can't follow that.

Can't follow what? That it's illegal, or that some compilers
accept it.
All g++-3.3.6, 4.2.1, and 4.3.0 make the first line in foo an
error and the second a warning without any special settings.

According to the standard, both are errors. The first was
allowed in very early versions of C++; it was found to cause
problems, and was banned sometime in the late 1980's (before the
standardization effort even began). I can understand compilers
in the early 1990's only giving a warning (so as not to break
existing code); I can even understand a compiler today having
options which would only make it a warning. But silently
accepting it, as Sun CC did? Something's wrong.

The second takes the address of an lvalue. That's never been
legal, not since K&R C. I can't understand any compiler not
making it a hard error.

(Note that the first can be made legal with a user defined
conversion to an lvalue in the class, and the second can be made
legal with a user defined operator&. Neither are present in the
example, however.)
The first line is the one that assigns reference to a
temporary to a non-const reference type, which cannot work.

Strictly speaking: neither line "assigns" anything. The first
line initializes a non-const reference with an rvalue; that's
forbidden by §8.5.3/5. The second takes the address of an
lvalue; that's forbidden by §5.3.1/2. (And it would also be an
initialization, rather than an assignment. Although in the case
of pointers, unlike references, the difference is neglible.)
The second line seems to have the same problem with const
pointer and non-const pointer at first.
liftim1.cc: In function ?void foo()?:
liftim1.cc:12: error: invalid initialization of non-const reference of
type ?A&? from a temporary of type ?B?
liftim1.cc:13: warning: taking address of temporary
A temporary object is not a const object by definition if
there is no const in the declaration.

Which isn't really relevant here.
The standard mandates that assigning a temorary to a non-const
reference is not allowed, but this decision is kind of
deliberate, because it would produce hard to detect errors
most of the time.

It's very deliberate, because it actually did produce hard to
detect errors a lot of times.
But taking the pointer is not the same es taking a reference.
Using that pointer could be problematic, especially
dereferencing, but the heuristical warning is the best one can
hope for when taking the address alone.

The standard says quite clearly: "The result of the unary &
operator is a pointer to its operand. The operand shall be an
lvalue or a qualified-id." (The "qualified-id" is for pointers
to members.) This is taken almost directly from C: "The operand
of the unary & operator shall be either a function designator,
the result of a [] or unary * operator, or an lvalue that
designates an object that is not a bit-field and is not declared
with the register storage-class specifier." (This is taken from
C99, which is the only C standard which I have available here.
"result of a [] or unary * operator" was definitely not present
in C90; for the rest, however, the text is basically unchanged
from C90, and if memory serves me right, K&R C.)

In sum, this has never, ever been legal, and I can't see the
slightest reason for a compiler to accept it. Interestingly
enough, in both Sun CC and g++, it actually depends on the type:
both compilers treat something like:
int* p = foo() ;
(where foo() returns an int) as an error, and SunCC behaves
differently depending on whether there is inheritance or a user
defined constructor. None of which makes any sense to me: it's
an error, it has always been an error, and there's not the
slightest justification for any compiler in the world to treat
it otherwise.
 
B

Bernd Strieder

Hello,

James said:
Bernd said:
James Kanze wrote:
Can't follow that.

Can't follow what? That it's illegal, or that some compilers
accept it.
All g++-3.3.6, 4.2.1, and 4.3.0 make the first line in foo an
error and the second a warning without any special settings.

According to the standard, both are errors. The first was
allowed in very early versions of C++; it was found to cause
problems, and was banned sometime in the late 1980's (before the
standardization effort even began). I can understand compilers
in the early 1990's only giving a warning (so as not to break
existing code); I can even understand a compiler today having
options which would only make it a warning. But silently
accepting it, as Sun CC did? Something's wrong.

The second takes the address of an lvalue. That's never been
legal, not since K&R C. I can't understand any compiler not
making it a hard error.

(Note that the first can be made legal with a user defined
conversion to an lvalue in the class, and the second can be made
legal with a user defined operator&. Neither are present in the
example, however.)
The first line is the one that assigns reference to a
temporary to a non-const reference type, which cannot work.

Strictly speaking: neither line "assigns" anything. The first
line initializes a non-const reference with an rvalue; that's
forbidden by §8.5.3/5. The second takes the address of an
lvalue; that's forbidden by §5.3.1/2. (And it would also be an
initialization, rather than an assignment. Although in the case
of pointers, unlike references, the difference is neglible.)
The second line seems to have the same problem with const
pointer and non-const pointer at first.
liftim1.cc: In function ?void foo()?:
liftim1.cc:12: error: invalid initialization of non-const reference
of type ?A&? from a temporary of type ?B?
liftim1.cc:13: warning: taking address of temporary
A temporary object is not a const object by definition if
there is no const in the declaration.

Which isn't really relevant here.
The standard mandates that assigning a temorary to a non-const
reference is not allowed, but this decision is kind of
deliberate, because it would produce hard to detect errors
most of the time.

It's very deliberate, because it actually did produce hard to
detect errors a lot of times.
But taking the pointer is not the same es taking a reference.
Using that pointer could be problematic, especially
dereferencing, but the heuristical warning is the best one can
hope for when taking the address alone.

The standard says quite clearly: "The result of the unary &
operator is a pointer to its operand. The operand shall be an
lvalue or a qualified-id." (The "qualified-id" is for pointers
to members.) This is taken almost directly from C: "The operand
of the unary & operator shall be either a function designator,
the result of a [] or unary * operator, or an lvalue that
designates an object that is not a bit-field and is not declared
with the register storage-class specifier." (This is taken from
C99, which is the only C standard which I have available here.
"result of a [] or unary * operator" was definitely not present
in C90; for the rest, however, the text is basically unchanged
from C90, and if memory serves me right, K&R C.)

That makes it clear. I have to admit that I never got the time to get
into the very details of C and C++ standards that much. But then again
it is again a deliberate decision. The operation that is always
possible on a returned object should be an ordinary copy constructor
taking a const reference. When calling that copy constructor defined in
another translation unit, in the general case a compiler has to handle
a pointer to that temporary non-lvalue, internally. So in fact that
pointer exists on a level below C++, and the compiler has some sensible
pointer for the unary & to return. Breaking the standard when allowing
it, is still out of question.

What do we do with this modification of the OP example

class A{
public:
A(const A&);
A(const A*); // commodity copy c'tor
A();
int x;
};

void foo()
{
A a1 = A(); // ok
A a2 = &A(); // error ?
}

When looking at the data at runtime, there would be two temporary A
objects and the copy c'tor would get the address of its object passed
to copy. The lifetime of the temporary ends after the assignment in
both cases. And most compilers are able to generate what we expect. IMO
there is a window where that pointer could be considered valid.

AFAIK C++ has even introduced some special rule to enlarge the lifetime
of a temporary when assigned to a const reference variable. So even
there has been some room of argument.

If a bug gets filed for the compilers, then it will probably be very low
priority, because that pointer is causing fatal errors with very high
probability, when stored and dereferenced later, which anybody using it
should have noticed, and at least g++ even produces a warning. And as
horribly as it seems, there might be legacy code out there relying on
the feature. That class A like above is a probable outcome of e.g.
former C programmer getting C++ programmer.

Bernd Strieder
 

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

Forum statistics

Threads
474,175
Messages
2,570,942
Members
47,490
Latest member
Finplus

Latest Threads

Top