Home arrow C++ arrow Page 4 - Temporary Variables: Runtime rvalue Detection
C++

Temporary Variables: Runtime rvalue Detection


In this final article in the series covering temporary variables, Jun Nakamura explains how you can test whether a variable is temporary or not, by using the ternary conditional operator.

Author Info:
By: J. Nakamura
Rating: 4 stars4 stars4 stars4 stars4 stars / 6
October 17, 2005
TABLE OF CONTENTS:
  1. · Temporary Variables: Runtime rvalue Detection
  2. · The Ternary Conditional Operator
  3. · The RVALUE_TEST macro
  4. · Determining Type

print this article
SEARCH DEVARTICLES

Temporary Variables: Runtime rvalue Detection - Determining Type
(Page 4 of 4 )

The problem compiler writers face when implementing the ternary conditional operator is that it needs a type.

condition ? expressionA : expressionB

 Since this is an expression, its result (either the outcome of expressionA or the outcome of expressionB) can be used in an assignment statement… thus the conditional operator must have a type. This type completely depends on either branch of the condition (expressionA or expressionB)… since these two can be completely different it is very difficult to figure out which type to use for the conditional operator.

Lets consider expressionA of typeA and expressionB of typeB, and that typeA and typeB are different, where at least one is a class type. This is what the standard says about deducing which type to use for the conditional expression (5.16/3):

Otherwise, if the second and third operand have different types, and either has (possibly cv-qualified) class type, an attempt is made to convert each of those operands to the type of the other.

The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows:

if E2 is an lvalue : E1 can be converted to match E2 if E1 can be implicitly converted (clause4) to the type “reference to T2”, subject to the constraint that in the conversion the reference must bind directly (8.5.3) to E1.

So the compiler will check to see whether it can convert typeA into a reference to typeB and whether typeB can be converted into a reference to typeA. When we look at the RVALUE_TEST(cout) test, we know that cout is an lvalue of type ostream… so how did the macro come to the same conclusion?

Well, the compiler first tries to convert the expression rvalue_probe() to the type “reference to ostream” (remember that ostream is our "container" in the macro). This succeeds, because rvalue_probe defines a conversion operator to L & for any L. When this operator is called it throws the string "lvalue" which is then captured and printed.

template < class L > operator L&() const { throw “lvalue”; }

Unfortunately the Microsoft compilers haven’t been too strong with template trickery (just check the number of work-arounds needed in the boost library) and the Visual C++ 7.1 compiler doesn’t compile this correctly. The code is correct however, since it follows the C++ Standard.

More interesting is to see how the macro was able to deduce that the string “test” in RVALUE_TEST(“test”) is a rvalue. That is what we are after!

To continue with what the C++ Standard has to say about the ternary conditional operator:

If E2 is an rvalue, or if the conversion above cannot be done:

if E1 and E2 have class type, and the underlying class types are the same or one is a base class of the other: E1 can be etc. etc.

Otherwise (i.e., if E1 or E2 has a nonclass type, or if they both have class types but the underlying classes are not either the same or one a base class of the other): E1 can be converted to match E2 if E1 can be implicityly converted to the type that expression E2 would have if E2 were converted to an rvalue (or the type it has, if E2 is an rvalue).

Wow! Luckily Eric Niebler shows that it is actually simpler than it sounds (if you could finish that sentence with one breath that is).

If the expression is an rvalue of type R (in this case “test” is an rvalue of type char const*), then the compiler should try to convert rvalue_probe() to an rvalue of type R (i.e. E1 can be implicitly converted to the type of E2). How does this happen?

This is where the operator R() comes in.

template < class R > operator R () { throw “rvalue”; }

Niebler explains that there are two ways for this to happen:

  • The compiler chooses rvalue_probe::operator R()
  • The compiler uses rvalue_probe::operator L&() and then uses the standard lvalue-to-rvalue conversion to achieve the same result.

The compiler performs overload resolution to pick the best conversion sequence with the end result that operator R() is chosen over operator L&() const!

It is amazing to see what can be achieved with C++ when you understand the C++ Standard and how compilers work. Unfortunately this is highly arcane stuff for most of us, though it is great to see it all in action!

I definitely recommend the original article [Niebler] and suggest you try the BOOST_FOREACH macro for yourself. It greatly simplifies iteration over STL containers and the boost version works with Visual C++ 7.1 as well. You no longer need std::for_each or custom iteration like:

list<X>::iterator it;
for (it=mylist.begin(); it!=mylist.end(); ++it) {
 cout << *it << endl;

Instead you can write:

BOOST_FOREACH(X x, mylist) { cout << x << endl; }

Because it can detect whether something is an rvalue or not, it is perfectly safe to print “Hello World” like this:

BOOST_FOREACH(char c, “Hello World”) { cout << c; }

Great stuff! My thanks go to Eric Niebler for letting me summarize this technique.


References

 [Meyers] – Scott Meyers

“Effective C++” – ISBN 0201924889

[Sutter] – Herb Sutter

“Exceptional C++” -  ISBN 0201615622

[Niebler] – Eric Niebler

“Conditional Love: FOREACH Redux”

http://www.artima.com/cppsource/foreach.html

[Kernighan] – Brian Kernighan (interview)

http://www-2.cs.cmu.edu/~mihaib/kernighan-interview/

The C++ Standard

http://www.ncits.org/

 
DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.

blog comments powered by Disqus
C++ ARTICLES

- Intel Threading Building Blocks
- Threading Building Blocks with C++
- Video Memory Programming in Text Mode
- More Tricks to Gain Speed in Programming Con...
- Easy and Efficient Programming for Contests
- Preparing For Programming Contests
- Programming Contests: Why Bother?
- Polymorphism in C++
- Overview of Virtual Functions
- Inheritance in C++
- Extending the Basic Streams in C++
- Using Stringstreams in C++
- Custom Stream Manipulation in C++
- General Stream Manipulation in C++
- Serialize Your Class into Streams in C++

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials