"Anonyomous" Objects

N

none

I am having great troubles with "anonymous" objects.
That might not be the correct term, but I just mean an
object created on the stack but never given a name.
These objects apparently do not follow the rules of
virtual inheritance.

The code below outputs the following:

B::func()
B::func()
A::func()
A::func()

The behavior is the same on both g++ and in MS Visual
Studio 2005.

The most obvious workaround would be to avoid using
anonymous objects. Unfortunately, I really NEED to use
them. I can explain the details of why I need to do
this, if it matters.

I've spent a day experimenting and I've found that the
behavior is very fickle. For example, removing the
constructor and destructor from class A changes the
output to:

B::func()
B::func()
B::func()
B::func()

Can anyone explain what's going on? Thanks in advance.



----- begin code: -----

#include "stdio.h"

class A
{
public:
A() {}
virtual ~A() {}
virtual void func() { printf("A::func()\n"); }
};

class B: public A
{
public:
void func() { printf("B::func()\n"); }
};

class C: public B
{
};

C x()
{
C new_C;
return new_C;
}

int main(int argc, char **argv)
{
// Call a method in an anonyomus object.
// This works.
x().func();

// Call a method in an anonymous object through a
pointer.
// This looks a little strange, but works.
(&(x()))->func();

// Should be exactly the same as above, but split
into two lines.
// This calls A::func() even though func() is
virtual!
A *A_ptr = &(x());
A_ptr->func();

// This also calls A::func(), even though the
pointer
// is of type "pointer-to-C"!!
C *C_ptr = &(x());
C_ptr->func();

return 0;
}

----- end code -----
 
T

Thomas J. Gritzan

none said:
I am having great troubles with "anonymous" objects.
That might not be the correct term, but I just mean an
object created on the stack but never given a name.

They are temporary objects. The name refers to their limited lifetime. A
temporary lives only until the end of the full expression in which it is
created.
These objects apparently do not follow the rules of
virtual inheritance.

They do. See below.
class A [...]
class B: public A [...]
class C: public B
{
};

C x()
{
C new_C;
return new_C;
}

int main(int argc, char **argv)
{
// Call a method in an anonyomus object.
// This works.
x().func();

This creates a temporary object, calls func on it and destroys the object.
// Should be exactly the same as above, but split
into two lines.
// This calls A::func() even though func() is
virtual!
A *A_ptr = &(x());

This creates a temporary object, takes its address and destroys the
object. The pointer is a dangling pointer and using it is a Bad
Idea[TM]; it is formally undefined behaviour.
A_ptr->func();

This line calls a member function on a non-existant object.
 
J

James Kanze

I am having great troubles with "anonymous" objects.

The correct name is "temporaries". Depending on the context,
the expression "rvalue" will also be used. (Technically,
objects are temporaries, and rvalue is a characteristic of an
expression. A temporary is created when an rvalue expression
results in an object.)
That might not be the correct term, but I just mean an
object created on the stack but never given a name.
These objects apparently do not follow the rules of
virtual inheritance.

The most certainly do. The only way they differ from other
objects is in their lifetime (to the end of the full
expression).
The code below outputs the following:

The behavior is the same on both g++ and in MS Visual
Studio 2005.

I get warnings when I compile with g++. In fact, the code is
illegal, and shouldn't compile.
The most obvious workaround would be to avoid using
anonymous objects. Unfortunately, I really NEED to use
them. I can explain the details of why I need to do
this, if it matters.
I've spent a day experimenting and I've found that the
behavior is very fickle. For example, removing the
constructor and destructor from class A changes the
output to:

Can anyone explain what's going on?

You have undefined behavior, so anything can happen.
----- begin code: -----
#include "stdio.h"
class A
{
public:
A() {}
virtual ~A() {}
virtual void func() { printf("A::func()\n"); }
};
class B: public A
{
public:
void func() { printf("B::func()\n"); }
};
class C: public B
{
};
C x()
{
C new_C;
return new_C;
}

Since you seem to be interested in temporary objects, you could
also just write:

C x()
{
return C();
}

The effect will be pretty much the same.
int main(int argc, char **argv)
{
// Call a method in an anonyomus object.
// This works.
x().func();
// Call a method in an anonymous object through a pointer.
// This looks a little strange, but works.
(&(x()))->func();

This shouldn't compile: "The result of the unary & operator is a
pointer to its operand. The operand shall be an lvalue or a
qualified-id"[§5.3.1/2] and "A function call is an lvalue if and
only if the result type is a reference"[§5.2.2/10]. I'm rather
surprised that any compiler accepts this, but both VC++ and g++
seem to (albeit with a warning in the case of g++); the
lvalue/rvalue distinction dates from the earliest days of C, and
you'd expect compilers to get anything this fundamental right.
// Should be exactly the same as above, but split into two lines.
// This calls A::func() even though func() is virtual!
A *A_ptr = &(x());
A_ptr->func();

Again, the first line shouldn't compile. In addition, the
lifetime of the temporary object in the first line is the end of
the full expression; after that, the object doesn't exist, and
any use of it is undefined behavior---anything can happen.
// This also calls A::func(), even though the pointer
// is of type "pointer-to-C"!!
C *C_ptr = &(x());
C_ptr->func();

Same problem as the above. You're using an object after it has
ceased to exist.
return 0;
}
----- end code -----

It might be interesting if you instrumented the classes, adding
explicit copy constructor, assignment, etc., and outputting what
is going on. You'll see that the destructor of the temporary
object is called at the end of the full expression in each case.
Technically, when the destructor of the derived class calls that
of the base class, the object takes on the type of the base
class; the compiler must ensure that during the destructor of
the base class, any virtual function calls (or use of typeid)
treats the object as being an object of the base class---in
practice, this means resetting the vptr. Depending on the code
generation strategy and the level of optimization, the compiler
may skip this step if it can determine that no virtual functions
are called in the destructor. This probably explains the
difference you see depending on whether you define a
(non-inlined) destructor for A, or the compiler provides its
default (inlined) destructor.

The compiler doesn't have to worry about anything which happens
to the object after the destructors have finished, since the
object may no longer be used.
 
M

Michael Tsang

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
#include "stdio.h"

class A
{
public:
A() {}
virtual ~A() {}
virtual void func() { printf("A::func()\n"); }
};

class B: public A
{
public:
void func() { printf("B::func()\n"); }
};

class C: public B
{
};

C x()
{
C new_C;
return new_C;
}

int main(int argc, char **argv)
{
// Call a method in an anonyomus object.
// This works.
x().func();

// Call a method in an anonymous object through a
pointer.
// This looks a little strange, but works.
(&(x()))->func();
How can you ever take the address of an r-value?
// Should be exactly the same as above, but split
into two lines.
// This calls A::func() even though func() is
virtual!
A *A_ptr = &(x()); Same as above.
A_ptr->func();

// This also calls A::func(), even though the
pointer
// is of type "pointer-to-C"!!
C *C_ptr = &(x()); Same as above.
C_ptr->func();

return 0;
}

----- end code -----
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)

iEYEARECAAYFAkr5UfEACgkQG6NzcAXitM+seACfVsffUKIfFS4ZOvjOE6F5CkLt
rJwAn2XrSLU1Q+0VgkOIUeZX+v3kEAPp
=EYkO
-----END PGP SIGNATURE-----
 
T

Triple-DES

[...]
int main(int argc, char **argv)
{
    // Call a method in an anonyomus object.
    // This works.
    x().func();
    // Call a method in an anonymous object through a pointer.
    // This looks a little strange, but works.
    (&(x()))->func();

This shouldn't compile: "The result of the unary & operator is a
pointer to its operand. The operand shall be an lvalue or a
qualified-id"[§5.3.1/2] and "A function call is an lvalue if and
only if the result type is a reference"[§5.2.2/10].  I'm rather
surprised that any compiler accepts this, but both VC++ and g++
seem to (albeit with a warning in the case of g++); the
lvalue/rvalue distinction dates from the earliest days of C, and
you'd expect compilers to get anything this fundamental right.

VC++8 gets this exactly right. With extensions enabled it outputs a
diagnostic:

"nonstandard extension used: class rvalue used as lvalue"

And if extensions are disabled, it is treated as an error:

"'&' requires l-value"
 
N

none

none said:
I am having great troubles with "anonymous" objects.
....

The most obvious workaround would be to avoid using
anonymous objects. Unfortunately, I really NEED to use
them.


Thanks for all the replies. I had a feeling that the answer would be
"you can't do that" before I asked.

So let me explain what I want to do and ask if it's even possible. I'd
like to create a C++ syntax for defining a BNF grammar (yes, I am aware
of boost::spirit). A line in a BNF grammar usually looks something like
this:


block: "{" declaration* statement* "}"


I'd like to be able to write this in C++ like so:


grammar_literal open_brace("{"), close_brace("}");
grammar_rule block, declaration, statement;

block = open_brace + *declaration + *statement + close_brace;


The catch is that the definition of "declaration" and "statement" might
happen after the definition of "block." This is impossible to avoid,
because BNF grammars are very commonly circular in nature, meaning that
there's no way to force the author to define everything in an order such
that everything is defined before it is used to define something else.

So, my idea was to have the "=" operator in grammar_rule store pointers
to the rvalues. The operators would be overloaded to return
grammar_rule objects (or possibly something derived from it) and the
assignment would store a pointer to that returned object, and the
grammar would be built up as a tree structure in this way.

Having the "=" operator perform a copy won't work (as far as I can see),
because later, when "declaration" or "statement" are defined, the tree
underneath "block" will not be updated.

Thanks again.
 
J

James Kanze

[...]
int main(int argc, char **argv)
{
// Call a method in an anonyomus object.
// This works.
x().func();
// Call a method in an anonymous object through a pointer.
// This looks a little strange, but works.
(&(x()))->func();
This shouldn't compile: "The result of the unary & operator
is a pointer to its operand. The operand shall be an lvalue
or a qualified-id"[§5.3.1/2] and "A function call is an
lvalue if and only if the result type is a
reference"[§5.2.2/10]. I'm rather surprised that any
compiler accepts this, but both VC++ and g++ seem to (albeit
with a warning in the case of g++); the lvalue/rvalue
distinction dates from the earliest days of C, and you'd
expect compilers to get anything this fundamental right.
VC++8 gets this exactly right. With extensions enabled it
outputs a diagnostic:
"nonstandard extension used: class rvalue used as lvalue"
And if extensions are disabled, it is treated as an error:
"'&' requires l-value"

Interesting. I thought it was VC++ 8 that I tried it with.

I'm also seem to remember having an error, and not just a
warning, with g++, in the past. But I'm just getting a warning
now.
 
J

James Kanze

So let me explain what I want to do and ask if it's even
possible. I'd like to create a C++ syntax for defining a BNF
grammar (yes, I am aware of boost::spirit). A line in a BNF
grammar usually looks something like this:
block: "{" declaration* statement* "}"
I'd like to be able to write this in C++ like so:
grammar_literal open_brace("{"), close_brace("}");
grammar_rule block, declaration, statement;
block = open_brace + *declaration + *statement + close_brace;
The catch is that the definition of "declaration" and "statement" might
happen after the definition of "block."

And the dynamic type might vary. (I don't know, but I can image
that that could be the case.)
This is impossible to avoid, because BNF grammars are very
commonly circular in nature, meaning that there's no way to
force the author to define everything in an order such that
everything is defined before it is used to define something
else.
So, my idea was to have the "=" operator in grammar_rule store
pointers to the rvalues. The operators would be overloaded to
return grammar_rule objects (or possibly something derived
from it) and the assignment would store a pointer to that
returned object, and the grammar would be built up as a tree
structure in this way.

It sounds to me like you need to deal with handles, and not
objects.
Having the "=" operator perform a copy won't work (as far as I
can see), because later, when "declaration" or "statement" are
defined, the tree underneath "block" will not be updated.

Exactly. You need reference semantics, which means some form of
handle (probably more than just a raw pointer here, but I'm not
sure).
 
N

none

James said:
It sounds to me like you need to deal with handles, and not
objects.

Handles, of course!

I'll allocate the objects on the heap. Sounds like I'll need some sort of
global "manager" to keep track of them. I think it should work out nicely.

Many thanks.
 
T

Triple-DES

[...]
VC++8 gets this exactly right. With extensions enabled it
outputs a diagnostic:
  "nonstandard extension used: class rvalue used as lvalue"
And if extensions are disabled, it is treated as an error:
  "'&' requires l-value"

Interesting.  I thought it was VC++ 8 that I tried it with.

I'm also seem to remember having an error, and not just a
warning, with g++, in the past.  But I'm just getting a warning
now.

Well, this (strictly conforming) behavior only manifests itself with
the highest level of warnings enabled (/W4). The extension in question
is for compatibility with the ancient VC6, which uncritically allowed
taking the address of a temporary.
 
T

Triple-DES

Triple-DES said:
VC++8 gets this exactly right. With extensions enabled it
outputs a diagnostic:
  "nonstandard extension used: class rvalue used as lvalue"
And if extensions are disabled, it is treated as an error:
  "'&' requires l-value"
Interesting.  I thought it was VC++ 8 that I tried it with.
I'm also seem to remember having an error, and not just a
warning, with g++, in the past.  But I'm just getting a warning
now.
Well, this (strictly conforming) behavior only manifests itself with
the highest level of warnings enabled (/W4). The extension in question
is for compatibility with the ancient VC6, which uncritically allowed
taking the address of a temporary.

Issuing a warning is all that's required of a conforming implementation.
The standard requires "a diagnostic". It does not require that the
compiler refuse to compile the code. (The only thing that is required to
terminate compilation is a #error directive).

Yes, I was referring to the fact that cl will not issue a diagnostic
unless it is invoked with either /W4, /Za, or both. So depending on
the combination of flags, it will either:

Issue a warning (OK)
Issue an error (OK)
Compile the code without warning or error (NOK)

I wasn't aware that "render[ing] the program ill-formed" as in the
case of #error was synonymous with termination. My understanding was
that the implementation is required to issue a diagnostic (unless it's
explicitly stated that "no diagnostic [is] required"), but might
produce an executable with undefined behavior.
 
N

none

Juha said:
none wrote:
I think that what's technically happening there is that A_ptr is
pointing to an object which gets destroyed at the first ';', after
which the function call is now operating on a destroyed "ghost"
? object.

Interesting! I agree with others who have said that this should be
treated as a compile-time error.

I have a follow-up question that is related to this behavior. I've
switched everything to deal with handles and I have a "manager" class
that keeps track of all the objects, both temporaries and otherwise.

Using the same example as before, with classes A, B, and C, what I'm
trying to do is have the constructor for class A simply add "this" to
the global manager class, like so:


----- begin code -----

my_global_object_manager_class g_mgr;

class A
{
public:
A() { m_handle = g_mgr.insert(this); }
virtual ~A() {}
virtual void func() { printf("A::func()\n"); }

private:
int m_handle;
};

class B: public A
{
public:
void func() { printf("B::func()\n"); }
};

class C: public B
{
};

int main(int argc, char **argv)
{
// The constructor for A will insert my_C into g_mgr
C my_C;

// Get a pointer to my_C through the manager
A *A_ptr = g_mgr.get_object_by_handle(0);

// This should call my_C.func()
A_ptr->func();
}

----- end code -----


I'm seeing this code display "A::func()". There's nothing fancy about
"my_global_object_manager_class", it's basically just a std::vector of
pointers-to-A. Am I wrong to think that B::func() should be called
here? Is it not valid to use the "this" pointer of a base class in this
way? I have only tried this in Visual Studio 2005.
 
N

none

none said:
I have a follow-up question that is related to this behavior.
....

Is it not valid to use the "this" pointer of a base class in this
way?

Please disregard this. After much debugging, I've found that this is just
a poor approach. Constructors are a bad place for this operation because
apparently there are more temporary objects constructed than I expected.

For example, I've overloaded the "+" operator and code like this:

C c1, c2;
C c3 = c1 + c2;

results in more calls to C::C() than I expected and my "manager" object
ends up filled with pointers to temporaries. And pointers to temporaries
are bad for all the reasons discussed earlier in this thread.

This is a nasty problem that is probably going to require a complete
redesign.
 
J

James Kanze

Juha Nieminen wrote:
I have a follow-up question that is related to this behavior.
I've switched everything to deal with handles and I have a
"manager" class that keeps track of all the objects, both
temporaries and otherwise.

You shouldn't have any temporaries. Normally, when using a
handle, everything is allocated on the stack. (Also, what you
are doing isn't quite what I meant by handle. The handle I was
thinking of was more along the lines of a smart pointer.)

Using the same example as before, with classes A, B, and C,
what I'm trying to do is have the constructor for class A
simply add "this" to the global manager class, like so:
----- begin code -----
my_global_object_manager_class g_mgr;
class A
{
public:
A() { m_handle = g_mgr.insert(this); }
virtual ~A() {}
virtual void func() { printf("A::func()\n"); }
private:
int m_handle;
};
class B: public A
{
public:
void func() { printf("B::func()\n"); }
};
class C: public B
{
};
int main(int argc, char **argv)
{
// The constructor for A will insert my_C into g_mgr
C my_C;

// Get a pointer to my_C through the manager
A *A_ptr = g_mgr.get_object_by_handle(0);

// This should call my_C.func()
A_ptr->func();
}
----- end code -----
I'm seeing this code display "A::func()". There's nothing
fancy about "my_global_object_manager_class", it's basically
just a std::vector of pointers-to-A. Am I wrong to think that
B::func() should be called here? Is it not valid to use the
"this" pointer of a base class in this way? I have only tried
this in Visual Studio 2005.

I don't see anything wrong here; I'd have to see the exact code
of the manager to be sure. As long as you don't copy the object
anywhere, there should be no problem. With the following
implementation of my_global_object_manager_class, I get
B::func():

class my_global_object_manager_class
{
public:
int insert( A* obj )
{
int result = my_objects.size();
my_objects.push_back( obj );
return result;
}
A* get_object_by_handle( int handle ) const
{
assert( handle >= 0 && handle < my_objects.size() );
return my_objects[ handle ];
}
private:
std::vector< A* > my_objects;
};

(Compiled with VC++ 8, but unless there's a really serious
compiler bug, you should get the same with any compiler.)

BTW: if you're using this strategy, you should also deregister
the object in its destructor. What I had in mind was something
more like the letter/envelope idiom. (The letter/envelope idiom
is a special case of a handle, in which the handle is the base
class of the polymorphic hierarchy.)
 
N

none

James said:
I don't see anything wrong here; I'd have to see the exact code
of the manager to be sure. As long as you don't copy the object
anywhere, there should be no problem. With the following
implementation of my_global_object_manager_class, I get
B::func():

class my_global_object_manager_class
{
public:
int insert( A* obj )
{
int result = my_objects.size();
my_objects.push_back( obj );
return result;
}
A* get_object_by_handle( int handle ) const
{
assert( handle >= 0 && handle < my_objects.size() );
return my_objects[ handle ];
}
private:
std::vector< A* > my_objects;
};

This is almost exactly my code. I must have a bug elsewhere? I will put
together a full example that reproduces the incorrect behavior.

BTW: if you're using this strategy, you should also deregister
the object in its destructor. What I had in mind was something
more like the letter/envelope idiom. (The letter/envelope idiom
is a special case of a handle, in which the handle is the base
class of the polymorphic hierarchy.)

I like the sound of this better. I will look into letter/envelope.

Thanks.
 
S

Stefan Große Pawig

Pete Becker said:
Triple-DES said:
I wasn't aware that "render[ing] the program ill-formed" as in the
case of #error was synonymous with termination. My understanding was
that the implementation is required to issue a diagnostic (unless it's
explicitly stated that "no diagnostic [is] required"), but might
produce an executable with undefined behavior.

I could have sworn that there was a special rule for #error, but I
can't find it. So pretend that I didn't say that #error requires
teminating compilation.

I guess you remembered it from C. At least the C99 standard says in the
"Conformance" section:
4/4: The implementation shall not successfully translate a preprocessing
translation unit containing a #error preprocessing directive unless
it is part of a group skipped by conditional inclusion.

I don't know whether this already existed in C90. But even if it did, I
think it would not apply to C++, since the C++ standard does not
reference the C conformance section. Is this understanding correct?

Kind regards,
Stefan
 

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,582
Members
45,071
Latest member
MetabolicSolutionsKeto

Latest Threads

Top