10

I read about std::is_pointer in C++.

Then I wrote the program and check whether T is a pointer type or not using std::is_pointer.

#include <iostream>

int main() 
{
    std::cout<<std::boolalpha;
    std::cout<<"char : " <<std::is_pointer<char>::value<<std::endl; // OK
    std::cout<<"char * : "<<std::is_pointer<char *>::value<<std::endl; // OK
    std::cout<<"char ** : "<<std::is_pointer<char **>::value<<std::endl; // OK
    std::cout<<"char *** : "<<std::is_pointer<char ***>::value<<std::endl; // OK
    std::cout<<"std::nullptr_t : "<<std::is_pointer<std::nullptr_t>::value<<std::endl;  // Not ok, Why false??
}

Output : [Wandbox Demo]

char : false
char * : true
char ** : true
char *** : true
std::nullptr_t : false

Why is std::is_pointer<std::nullptr_t>::value equal to false?

msc
  • 33,420
  • 29
  • 119
  • 214

5 Answers5

20

Because std::nullptr_t is not a pointer type. And is_pointer only evaluates to true for pointer types.

nullptr_t is convertible to a pointer, but it isn't a pointer. Indeed, nullptr_t is not a class type, integral, floating-point, enumerator, or any kind of type other than is_null_pointer type. It has its own unique classification in the categorization of types.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 3
    Any Idea about the rational behind that design decision? – MikeMB Nov 14 '17 at 08:27
  • 2
    @MikeMB Yeah, so it is not accidentally deduced as anything else in template overload resolution. having int `0` being a null pointer content was a mess – M.M Nov 14 '17 at 08:43
  • `nullptr_t` is *not* a class type? Is that explicitly mandated? [lex.nullptr] only states that it is not a pointer type. – Quentin Nov 14 '17 at 09:54
  • @M.M: I absolutely agree that the special semantics of `0`are very problematic, but I would have imagined / hoped that `nullptr` could be it's own type, but that type would be a pointer type (just like `void*` is a pointer type with some special semantics). – MikeMB Nov 14 '17 at 10:19
  • 3
    @MikeMB: The problem with that would be that `template void f(T*); f(nullptr)` would then be valid. Currently, if you want to allow that call, you must add an overload `void f(std::nullptr_t)` but that overload can forward to e.g. `f`. However, if `nullptr` was a pointer type and you did _not_ want it, removing it from the oveload set would be more complicated. – MSalters Nov 14 '17 at 13:01
  • 3
    @Quentin: "*Is that explicitly mandated?*" Yes. It is listed in [basic.fundamental], so it cannot be a class type. And `is_null_pointer` is a primary type category; for any type `T`, only *one* of the primary category traits will be true. – Nicol Bolas Nov 14 '17 at 14:23
9

Because it's not a pointer type. It's a distinct type that is implicitly convertible to any pointer type.

As the note in [lex.nullptr] summarizes:

The pointer literal is the keyword nullptr. It is a prvalue of type std​::​nullptr_­t. [ Note: std​::​nullptr_­t is a distinct type that is neither a pointer type nor a pointer to member type; rather, a prvalue of this type is a null pointer constant and can be converted to a null pointer value or null member pointer value. See [conv.ptr] and [conv.mem].  — end note ]

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
6

As unintuitive as it sounds, std::nullptr_t is not a pointer type. For instance, you cannot dereference a std::nullptr_t, so it'd be pretty weird if is_pointer_type<nullptr_t>::value was true.

It is merely convertible to any pointer type.

zneak
  • 134,922
  • 42
  • 253
  • 328
4

Because std::nullptr_t is not a pointer type itself, but the type of the null pointer literal nullptr.

std::nullptr_t is the type of the null pointer literal, nullptr. It is a distinct type that is not itself a pointer type or a pointer to member type.

From the standard, 21.2.3 Null pointers [support.types.nullptr]

The type nullptr_­t is a synonym for the type of a nullptr expression, and it has the characteristics described in [basic.fundamental] and [conv.ptr].

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
1

It's definitely unintuitive in some cases, but it makes sense:

  1. Value-wise, it doesn't behave like a pointer; it cannot point to anything.
    For example, you cannot do

    T *p = ...;
    reinterpret_cast<T *>(reinterpret_cast<std::nullptr_t>(p));
    

    and expect to get a pointer to the same address that p points to.

  2. Type-wise, it doesn't behave like a pointer. For example,

    std::is_pointer<P>::value
    

    should imply

    std::is_same<P, std::remove_pointer<P>::type *>::value
    

    but that clearly isn't the case.

user541686
  • 205,094
  • 128
  • 528
  • 886