Temporary object confusion

Started by
7 comments, last by Matsen 21 years, 2 months ago
Hello I''m having two questions regarding mojo, by Alexandrescu. First, I don''t understand when non const and const temporary objects are created. Reading about temporary objects in Thinking in C++, I get the impression that non const temporaries don''t exist? However, Alexandrescu talks about detecting them, so I''m really confused. Does a non const temporary exist or have I understand everything completely wrong? If not, it would be really helpful if someone could show me some simple code which produces a non const and a const temporay object. Second, reading from Thinking in C++ again, this shouldn''t compile under VC6, but it does (gcc gives an error). Mojo uses this behaviour to detect a temporary object:

struct A
{};

void f(A&)
{}

// a call to f with a temporary object
f(A());
 
Is this behaviour prohibited by ISO (ansi?) C++ or is it up to the compiler? Best regards Mats
Advertisement
quote:Original post by Matsen
First, I don''t understand when non const and const temporary objects are created. Reading about temporary objects in Thinking in C++, I get the impression that non const temporaries don''t exist? However, Alexandrescu talks about detecting them, so I''m really confused. Does a non const temporary exist or have I understand everything completely wrong? If not, it would be really helpful if someone could show me some simple code which produces a non const and a const temporay object.

void f( const A & );void g( A & );...f( A() );  // the A object itself was non-const, but the reference is constg( A() );  // another example of a non-const temporary, this time accessed via a non-const reference...struct B{  const A createA() { return A(); }};...f( B::createA() ); // okay, const temporary bound to const referenceg( B::createA() ); // error: cannot bind const object to non-const reference 

Simply speaking, a temporary object is any object created for relatively short durations. In the examples you have given (I haven''t read the Mojo article yet), "temporaries" appears to refer to values that are not explicitly/directly manipulated because they are never declared/initialized to a variable, just instantiated and consumed. They are of interest because they can generate significant additional operations (think about the differences between pre- and post-increment where the return value is not consumed in the same statement), which is what I think Alexandrescu is addressing:
for( int i = 0; i < some_value; ++i ){  // whatever} 

While the difference between ++i and i++ is negligible in the case of integer incrementation, it can be significant in the case of, say, an iterator for a complex compound type. In that case, the fact that post-incrementation returns a temporary copy before performing the incrementation means that a copy constructor will be invoked - which can eat up a lot of cycles.

quote:Second, reading from Thinking in C++ again, this shouldn''t compile under VC6, but it does (gcc gives an error)... Is this behaviour prohibited by ISO (ansi?) C++ or is it up to the compiler?

ANSI, by the way. AFAICT, there''s nothing wrong with said code per se. Of course, A doesn''t actually declare or define a constructor, but remember that some compilers (like MSVC6) will supply default and copy constructors for you if you don''t (they allocate enough memory and do a bitwise copy, respectively). Other than that, f is simply a function consuming a temporary object (did you know that Java even allows for anonymous classes declared in a function parameter list?) Perfectly normal, if you ask me.
quote:Original post by Matsen
Is this behaviour prohibited by ISO (ansi?) C++ or is it up to the compiler?


Both ISO and ANSI have adopted the same version of the C++ standard. In fact, ANSI J16 (the ANSI C++ committee) and ISO WG21 (the ISO C++ work group) are effectively the same body.

Also your example should compile on a standards compliant compiler. If you decompose the statement f(A()), the A() should result in a temporary created like A some_name() that then returns some_name as a r-value (5.2.3 paragraph 1 in the standard). This r-value should then be used to copy construct a temporary which is then bound to the reference (12.2 pargraph 1). The original temporary constructed should not be cv-qualified (declared const or volatile).

Incidently, this seems to be an error in gcc''s conversion capabilities (specifically, I believe, in creating an non-const reference from an r-value). A functional-notation defined temporary is still non-const in gcc (at least gcc 3.2). You can test this by changing your test case to:

struct A {  A & ref() { return *this; }};void f(A &) {}f( A().ref() ); 


If the temporary created by A() was const, then ref() could not be called as it is a non-const member function of A().
quote:Original post by Oluseyi
Of course, A doesn''t actually declare or define a constructor, but remember that some compilers (like MSVC6) will supply default and copy constructors for you if you don''t (they allocate enough memory and do a bitwise copy, respectively).


This is an over-simplification. First off, all standard compliant compilers will define a default constructor if no other constructor is defined. Even if you define one other constructor, the default constructor will not be defined (and if that happens to be the copy constructor, then neither will be created by the compiler).

Secondly, the default constructor does not allocate memory; it''s equivalent to a declared constructor with no parameters, no member initialization list and an empty function body, which will still invoke the default constructors of any class type member variables.

Third, the default copy constructor does not perform a bitwise copy, it performs a memberwise copy. This means if a member variable is a class type that has a non-trivial copy constructor, that will be invoked (very important if you have an std::auto_ptr member variable).

Also, there is no need to put the term temporaries in quotes. Temporary objects are well defined and even have their own section in the standard.
Hi, and thanks for the replies. I''m even more confused now, so with the risk of beeing flamed to pieces for stupidity...

quote:]Thinking in C++
... But there is one thing about temporaries: they’re automatically const . Because you usually won’t be able to get your hands on a temporary object, telling it to do something that will change that temporary is almost certainly a mistake because you won’t be able to use that information. By making all temporaries automatically const, the compiler informs you when you make that mistake.
... By making all temporary objects automatically const, this situation causes a compile-time error so you don’t get caught by what would be a very difficult bug to find.


quote:Original post by SiCrane
Also your example should compile on a standards compliant compiler.


"this situation", mentioned in the first quote, refers to:
struct A {};A getA() {return A();}void f(A& a){}// call f with a reference to a temporary f(getA()); 


...which is essentially the same thing as the same code as in my second question or void g(A()) in Oluseyi''s example. The code doesn''t compile under GCC and according to Thinking in C++, it shouldn''t. Saying A().foo(), as in SiCranes example, where foo() is a non const member function, does compile, but then it would be incorrect to say that all temporaries are const, wouldn''t it? Confusion. What am I missing?

quote:
Incidently, this seems to be an error in gcc''s conversion capabilities (specifically, I believe, in creating an non-const reference from an r-value). A functional-notation defined temporary is still non-const in gcc (at least gcc 3.2).


I don''t understand, could you elaborate?

quote:...which is what I think Alexandrescu is addressing

Mojo is a technique for eliminating unnessecary copying of dynamically allocated data, when passing objects by value.

quote:did you know that Java even allows for anonymous classes declared in a function parameter list?

Yeah, it looks really weird. But isn''t that possible only because Java allocates objects on the heap, which is later garbage collected?

Regards Mats
http://www.zib.de/benger/C%2B%2B/clause12.html#s12.2

No word on about constness of temporaries. Temporaries are const only if explicitely declared as such (like in Oluseyi''s example, a function that returns by const value)
First off, I made a mistake in my first post. It shouldn''t compile because rvalues can only be bound to const references. (Oops. I was misremembering a defect report in gcc where it was stating that the error message for converting rvalues to non-const references was incorrect.)

Next, I took a look at the "Thinking in C++" link, and I''ve come to the conclusion that the author is oversimplifying things to the point of confusion. At least he doesn''t seem to be making a distinction between rvalue and const temporary.

I''ll try to explain more clearly. Expressions are either rvalues or lvalues. The definitions are fairly complex, but basically an lvalue is anything you can take the address of and an rvalue is any other kind of expression. Most temporaries, such as the non-reference return values from functions, are rvalues. rvalues cannot be bound to non-const references (according to 8.5.3 paragraph 5 in the standard). However, rvalues can be both const and non-const. (More technically speaking rvalues can be freely cv-qualified.)

Using the examples from "Thinking in C++"

f7(f5()) fails, not because the the temporary returned by f5() is const, but because as an rvalue, the temporary cannot be bound to a non-const reference.

f5() = X(1) succeeds because it''s equivalent to f5().operator=(X(1)) where the implicitly defined assignment operator takes a const reference from the rvalue returned by the functional-notation temporary and the temporary returned by f5() is non-const.

f5().modify() succeeds because the temporary returned by f5() is non-const.

However if f5() was defined const X f5(), then both those cases would fail because the temporary returned would be const.

And a functional-notation temporary is a temporary created by taking a type name followed by a (possibly empty) parameter list. It''s defined to create an rvalue. In your example, A() creates a temporary rvalue of type A.
quote:Original post by SiCrane
Next, I took a look at the "Thinking in C++" link, and I''ve come to the conclusion that the author is oversimplifying things to the point of confusion...


I agree.

Thanks for your reply! I''m less confused

On a side note: does anyone have a link to where I can download the "C++ Final Draft International Standard"? Google only gave me online or old versions.

Regards Mats





Only draft copies are legally freely available. In order to get a final version you need to buy a copy. There is a link to buy it from the ISO website (http://www.iso.ch/iso/en/CatalogueDetailPage.CatalogueDetail?CSNUMBER=25845)
but it''s generally easier to get it from your nation''s standard''s organization. For people in the US it''s ANSI, but I think Sweden would be SIS (www.sis.se).

This topic is closed to new replies.

Advertisement