Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++03. Test for rvalue-vs-lvalue at compile-time, not just at runtime

Tags:

In C++03, Boost's Foreach, using this interesting technique, can detect at run-time whether an expression is an lvalue or an rvalue. (I found that via this StackOverflow question: Rvalues in C++03 )

Here's a demo of this working at run-time

(This is a more basic question that arose while I was thinking about this other recent question of mine. An answer to this might help us answer that other question.)

Now that I've spelled out the question, testing rvalue-ness in C++03 at compile-time, I'll talk a little about the things I've been trying so far.

I want to be able to do this check at compile-time. It's easy in C++11, but I'm curious about C++03.

I'm trying to build upon their idea, but would be open to different approaches also. The basic idea of their technique is to put this code into a macro:

true ? rvalue_probe() : EXPRESSION; 

It is 'true' on the left of the ?, and therefore we can be sure that EXPRESSION will never be evaluated. But the interesting thing is that the ?: operator behaves differently depending on whether its parameters are lvalues or rvalues (click that link above for details). In particular, it will convert our rvalue_probe object in one of two ways, depending on whether EXPRESSION is an lvalue or not:

struct rvalue_probe {     template< class R > operator       R () { throw "rvalue"; }     template< class L > operator       L & () const { throw "lvalue"; }     template< class L > operator const L & () const { throw "const lvalue"; } }; 

That works at runtime because the thrown text can be caught and used to analyze whether the EXPRESSION was an lvalue or an rvalue. But I want some way to identify, at compile-time, which conversion is being used.

Now, this is potentially useful because it means that, instead of asking

Is EXPRESSION an rvalue?

we can ask:

When the compiler is compiling true ? rvalue_probe() : EXPRESSION, which of the two overloaded operators, operator X or operator X&, is selected?

( Ordinarily, you could detect which method was called by changing the return types and getting the sizeof it. But we can't do that with these conversion operators, especially when they're buried inside the ?:. )

I thought I might be able to use something like

is_reference< typeof (true ? rvalue_probe() : EXPRESSION) > :: type 

If the EXPRESSION is an lvalue, then the operator& is selected and I hoped that the whole expression would then be a & type. But it doesn't seem to work. ref types and non-ref types are pretty hard (impossible?) to distinguish, especially now that I'm trying to dig inside a ?: expression to see which conversion was selected.

Here's the demo code pasted here:

#include <iostream> using namespace std; struct X {         X(){} };  X x; X & xr = x; const X xc;        X   foo()  { return x; } const X   fooc() { return x; }       X & foor()  { return x; } const X & foorc() { return x; }  struct rvalue_probe {         template< class R > operator       R () { throw "rvalue"; }         // template< class R > operator R const () { throw "const rvalue"; } // doesn't work, don't know why         template< class L > operator       L & () const { throw "lvalue"; }         template< class L > operator const L & () const { throw "const lvalue"; } };  typedef int lvalue_flag[1]; typedef int rvalue_flag[2]; template <typename T> struct isref     { static const int value = 0; typedef lvalue_flag type; }; template <typename T> struct isref<T&> { static const int value = 1; typedef rvalue_flag type; };  int main() {         try{ true ? rvalue_probe() : x;       } catch (const char * result) { cout << result << endl; } // Y lvalue         try{ true ? rvalue_probe() : xc;      } catch (const char * result) { cout << result << endl; } // Y const lvalue         try{ true ? rvalue_probe() : xr;      } catch (const char * result) { cout << result << endl; } // Y       lvalue         try{ true ? rvalue_probe() : foo();   } catch (const char * result) { cout << result << endl; } // Y rvalue         try{ true ? rvalue_probe() : fooc();  } catch (const char * result) { cout << result << endl; } // Y rvalue         try{ true ? rvalue_probe() : foor();  } catch (const char * result) { cout << result << endl; } // Y lvalue         try{ true ? rvalue_probe() : foorc(); } catch (const char * result) { cout << result << endl; } // Y const lvalue  } 

(I had some other code here at the end, but it's just confusing things. You don't really want to see my failed attempts at an answer! The above code demonstrates how it can test lvalue-versus-rvalue at runtime.)