Unexpected behaviour of std::move on T* type in C++

146 Views Asked by At

I have below code snippet where i declare a variable called pval which is attempting to derive T&& on a T* [ with T being int ]. As per type information [ decoded using abi ] the type derived is int*.

But when I compare the int* type with decltype(pval) it returns zero rather than 1 which means it treats pval as different type other than int*. So which one is the wrong pval being int* as reported by typeid or is_same which indicates comparison as false.

#include<iostream>
#include<string>
#include<typeinfo>
#include<cxxabi.h>
#include<type_traits>

using namespace std;

std::string classname(const std::type_info& info)
{
    int status;
    char* rslt=abi::__cxa_demangle(info.name(),0,0,&status);
    std::string result(rslt);
    free(rslt);
    return result;
}

int main(int argc, char* argv[])
{
    int* ptr = new int(10);
    decltype(std::move(ptr)) pval = std::move(ptr);
    cout << classname(typeid(pval)) << endl;             // as per typeid information the type of pval is int*.

    bool isSame = is_same<decltype(pval), int*>::value;  // What then is the pval not same as int* as per is_same ? 
    cout << "isSame status = " << isSame << endl;
    cout << is_same<int*, int*>::value << endl;
    return(0);
}
2

There are 2 best solutions below

1
On BEST ANSWER

The behaviors of decltype and typeid are different.

The exact type of pval is int* &&, i.e. an rvalue-reference to int*. (That's why std::is_same returns false when comparing it with the type of int*.) According to the behavior of decltype,

if the value category of expression is xvalue, then decltype yields T&&;

And what std::move(ptr) returns is an xvalue.

The following expressions are xvalue expressions:

  • a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);

Then given decltype(std::move(ptr)) pval, the type of pval would be int* &&.

On the other hand, the behavior of typeid is different.

Refers to a std::type_info object representing the type type. If type is a reference type, the result refers to a std::type_info object representing the referenced type.

That means the std::type_info object returned by typeid(pval) will refer to the referenced type, i.e. int*, not int* &&.


BTW: What std::type_info::name returns is implementation defined.

1
On

The __cxa_demangle() function doesn't give you reliable (or any?) information about const and reference qualifiers. Try this instead of your classname() function:

template <typename T, bool WithCVCorrections = true>
std::string type_name()
{
    typedef typename std::remove_reference<T>::type TR;

    std::unique_ptr<char, void(*)(void*)> own(
    abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr),
        std::free
    );
    std::string r = (own != nullptr) ? own.get() : typeid(TR).name();
    if (WithCVCorrections) {
        if (std::is_const<TR>::value)
            r += " const";
        if (std::is_volatile<TR>::value)
            r += " volatile";
        if (std::is_lvalue_reference<T>::value)
            r += "&";
        else if (std::is_rvalue_reference<T>::value)
            r += "&&";
    }
    return r;
}

... which is based on Howard Hinnant's code here. Obvious caveat: This will only work for some compilers (not MSVC).