0

I am trying to learn rvalue references, as an exercise I tried to do answer the following.

Is it possible to write a function that can tell (at least at runtime, better if at compile time) if the passed value is a value (non reference), a rvalue or an lvalue? for a generic type? I want to extract as much information about the type as possible.

An alternative statement of the problem could be:

Can I have a typeid-like function that can tell as much as possible about the calling expression?, for example (and ideally) if the type is T, T&, T const&, or T&&. Currently, for example, typeid drops some information about the type and one can do better (as in the example the const and non-const reference are distiguished). But how much better than typeid can one possibly do?

This is my best attempt so far. It can't distinguish between a rvalue and a "constant". First and second case in the example).

Maybe distiguishing case 1 and 2 is not possible in any circumstance? since both are ultimately rvalue? the the question is Even if both are rvalues can the two cases trigger different behavior?

In any case, it seems I overcomplicated the solution as I needed to resort to rvalue conditional casts, and ended up with this nasty code and not even 100% there.

#include<iostream>
#include<typeinfo>

template<class T>
void qualified_generic(T&& t){
    std::clog << __PRETTY_FUNCTION__ << std::endl;
    std::clog 
        << typeid(t).name() // ok, it drops any qualification
        << (std::is_const<typename std::remove_reference<decltype(std::forward<T>(t))>::type>::value?" const":"") // seems to detect constness rigth
        << (std::is_lvalue_reference<decltype(std::forward<T>(t))>::value?"&":"")
        << (std::is_rvalue_reference<decltype(std::forward<T>(t))>::value?"&&":"") // cannot distiguish between passing a constant and an rvalue expression
        << std::endl
    ;
}


using namespace std;
int main(){
    int a = 5;
    int const b = 5;

    qualified_generic(5); // prints "int&&", would plain "int" be more appropriate?
    qualified_generic(a+1); // prints "int&&" ok
    qualified_generic(a); // print "int&", ok
    qualified_generic(b); // print "int const&", ok
}

Maybe the ultimate solution to distiguish between the cases will involve detecting a constexpr.

UPDATE: I found this talk by Scott Meyers where he claims that "The Standard sometimes requires typeid to give the 'wrong' answer". http://vimeo.com/97344493 about minute 44. I wonder if this is one of the cases.

UPDATE 2015: I revisited the problem using Boost TypeIndex and the result is still the same. For example using:

template<class T>
std::string qualified_generic(T&& t){
    return boost::typeindex::type_id_with_cvr<decltype(t)>().pretty_name();
    // or       return boost::typeindex::type_id_with_cvr<T>().pretty_name();
    // or       return boost::typeindex::type_id_with_cvr<T&&>().pretty_name();
    // or       return boost::typeindex::type_id_with_cvr<T&>().pretty_name();
}

Still it is not possible to distinguish the type of 5 and a+1 in the above example.

alfC
  • 14,261
  • 4
  • 67
  • 118
  • It's unclear what you mean by "value", when you follow it up with the value categories "lvalue" and "rvalue". In C++03, every "value" falls into one of those categories (and in C++11, one of those or several more). "Value" on its own, then, would appear to have no meaning here. Perhaps you could explain what your _actual_ problem is. – Lightness Races in Orbit Jan 23 '14 at 02:57
  • I will remark that in the question but basically, the actual problem is to have a `typeid`-like function that can tell *as much as possible about the enclosed expression*. (For example, it certainly can tell the different between a `const` and a non-`const` lvalue reference for example, information which currently `typeid` drops). the "*as much us possible*" makes the solution contingent, I think, to what can be actually achieved by the current language (C++11). It is a theoretical exercise at the moment, but I think it is a well defined problem. – alfC Jan 23 '14 at 03:02
  • 2
    Is this what you're looking for? http://stackoverflow.com/a/20721887/576911 – Howard Hinnant Jan 23 '14 at 03:32
  • @HowardHinnant. Probably yes. Maybe the problem is that a notation `T`, `T&&`, etc, cannot distinguish expressively between `pxvalue` and `prvalue`. Do you think that can be converted to function like call, or the use of `decltype` at the call location is mandatory to not loose type information. Funny that `5` appears in both examples (and not `42`). – alfC Jan 23 '14 at 03:43
  • @HowardHinnant, your solution in the other question is even less sensitive than my attempt. Since the first three cases return the same output "prvalue expression of type int". [`gcc 4.8.2`]. – alfC Jan 23 '14 at 03:57
  • @HowardHinnant, correction, if I use the double parenthesis `decltype(())` then I get the same level or sensitivity as my attempt, case 1 and 2 are still the same, which makes me think that there is ultimatelly no different between `5` and `a+1`. Not even in principle. – alfC Jan 23 '14 at 04:00
  • 1
    If all you really want is to distinguish between lvalue and rvalue, I think you can do it without the decltype at the call site, much like you've shown. If you want to distinguish between lvalue, xvalue and prvalue, I think you need the decltype at the call site. I agree that there is no difference between the *expressions* 5 and a+1. These expressions are both prvalues. – Howard Hinnant Jan 23 '14 at 04:04
  • 3
    One must also be careful to distinguish between one of these two goals: 1. Are you looking for the type (and value category) of an expression?, or 2. Are you looking for the declared type of a variable or type-expression? The former is never a reference type, and instead includes a value category (lvalue, xvalue, prvalue). The latter has no value category, and may include an lvalue reference or rvalue reference type. In the latter case, this answer may be helpful: http://stackoverflow.com/a/20170989/576911. I got the impression you are looking for (1), but I could be mistaken. – Howard Hinnant Jan 23 '14 at 04:21
  • @HowardHinnant, 1) I think you answered the questions for the negative with your comments and links, that is that it is not possible to be more sensitive to type/category/qualifiers than my attempt. 2) Probably because there is no difference between the remaining case. 3) Maybe the only difference between `5` and `a+1` is that the former can be a `constexpr`, I wonder if the function can possibly distinguish between `constexpr` arguments and not constexpr but that deserves another question. – alfC Jan 23 '14 at 19:16
  • No, there is probably no way to determine whether an expression is `constexpr`, assuming you want to have the program compile either way. We've had a few questions about that before. – aschepler Jun 10 '14 at 00:47

0 Answers0