[Day 215] How to get a nonref type from decltype()

Hi all,

At 30:05 on day 215, we hit a compile error owing to the behavior of decltype(e).

For reference, the rule is here.

Basically: the idea is that if you give it a variable name (without extra parentheses), you just get the type of that variable. Otherwise, you get the type of the expression, except that in the case where the expression is an lvalue, the compiler adds '&' to reflect the lvalue-ness of the expression.

The original proposals for decltype (from 2006) motivate this behavior, but the use cases tend to involve templates exclusively.

Since we tend to avoid templates on HMH, we just want a way to remove the top-level ref. Unfortunately, the only way I know to do that is with a template. But it's a fairly small & isolated use and does not involve any libraries.

Here it is:
1
2
3
4
    template<class T>struct remove_ref     { typedef T type; };
    template<class T>struct remove_ref<T&> { typedef T type; };
    template<class T>struct remove_ref<T&&>{ typedef T type; };
    #define etype(e) remove_ref<decltype(e)>::type
And here's a sample use:
1
2
3
4
5
int r[10];
int x; 
int y;
etype(r[0]) z1 = 42; // ok (z1 is of type remove_ref<T&>::type, 
                     // a.k.a. int)
For completeness, here's the error condition we're trying to avoid:
1
2
decltype(r[0]) z2 = 42; // error (cannot initialize 'int&'
                        // with an rvalue)

One more thing: if you happen to be doing some metaprogramming (or other tasks that required you to write your own C parser from scratch), you probably won't want to bother with modifying your parser to support templates just for this. In that case, #ifdef it:
1
2
3
4
5
6
7
8
9
#if WE_DISLIKE_TEMPLATES
    #define etype(e) etype(e) // You'll want to teach 
                              // your parser about this.
#else
    template<class T>struct remove_ref     { typedef T type; };
    template<class T>struct remove_ref<T&> { typedef T type; };
    template<class T>struct remove_ref<T&&>{ typedef T type; };
    #define etype(e) remove_ref<decltype(e)>::type
#endif
On a personal note, I should apologize for not objecting to this behavior of decltype when I had the chance. As an explanation (but not an excuse): of all the users who showed up at the meetings who wanted to use decltype, I don't think there were any who also wanted to avoid templates. The two always seemed to go together, and macros tend to be less popular in that crowd.

On the internal wiki from 2006, I even see the note about using the remove_reference template to strip references, and today, there is an STL template that does exactly that (std::remove_reference).

There should have been a simpler operator for people who don't want to use templates, but that was not at the front of our minds, and that was a mistake.

Sorry, everyone.

Edited by James Widman on Reason: needed to actually say "sorry".
It is silly that you have to do this! Even the new move semantics C++11 require this template definition. You would have thought that they would have even implement this correctly!

decltype should be accompanied with basetype or typeof or something.

Edited by Ginger Bill on
gingerBill
It is silly that you have to do this!
Yes.
Even the new move semantics C++11 require this template definition.
Indeed, std::move() and std::forward() are specified in terms of std::remove_reference.

decltype should be accompanied with basetype or typeof or something.
Yes.