Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

checking constness using type_traits

I was working on a program recently which goes like this :-

#include <iostream>
#include <memory>
#include <type_traits>
#include <typeinfo>
using namespace std;

int main()
{
    shared_ptr<int> sp1 = make_shared<int>(5);
    shared_ptr<const int> sp2 (sp1);
    const int x = 8;

    // *sp2 = 7;   // error: assignment of read-only location 'sp2.std::shared_ptr<const int>::<anonymous>.std::__shared_ptr<_Tp, _Lp>::operator*<const int, (__gnu_cxx::_Lock_policy)2u>()'

    auto p = sp2.get();

    cout << typeid(x).name() << '\t' << typeid(*p).name() << '\n';

    cout << boolalpha;
    cout << is_const<decltype(*p)>::value << '\n' << is_same<const int, decltype(*p)>::value;

    return 0;
}

The output of this program is :-

i   i
false
false

As visible clearly, typeid specifies that *p & x are of same type & even using *sp2 = 7 generates an error. Then why do std::is_same & std::is_const differ from it ?

like image 433
Anwesha Avatar asked Aug 19 '26 19:08

Anwesha


1 Answers

You are running into two different issues here. First case we can see from the cppreference entry for typeid that top level const qualifiers are ignored:

In all cases, cv-qualifiers are ignored by typeid (that is, typeid(T)==typeid(const T))

We can see from the cppreference entry for decltype, the following rules that are relevant here are:

  1. f the argument is an unparenthesized id-expression or an unparenthesized class member access, then decltype yields the type of the entity named by this expression. [...]
  2. If the argument is any other expression of type T, and

[...]

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

[...]

So *p is an expression and is not a id-expression nor a class member access unlike let's say the following case:

const int x = 0 ;
std::cout << is_const<decltype(x)>::value << "\n" ; 
                      ^^^^^^^^^^^

Which would yield true since x is an id-expression.

So *p being an expression will yield T& since the result is an lvalue. The reference will not itself be const so is_const is correct, on the other hand this will return the result you desire:

is_const<std::remove_reference<decltype(*p)>::type>::value
         ^^^^^^^^^^^^^^^^^^^^^

Note the use of std::remove_reference.

T.C. point out that std::remove_pointer could also be effective here:

is_const<std::remove_pointer<decltype(p)>::type>::value
like image 104
Shafik Yaghmour Avatar answered Aug 21 '26 08:08

Shafik Yaghmour