Tuesday 1 April 2014

C++11 features - nullptr

1. Problem
In C++03 null pointer constant is defined as an integral constant expression, which is evaluated as 0. This means that integer 0 has two meanings. One is the integer 0 and another is the null pointer value. This often causes the trouble when encountering the function overloading.

void foo(int);
void foo(char*);

foo(0) will give the ambiguous option. Here "0" is an integer or a null pointer. In C++03 function overloading induction foo(int) will be called. In order to call foo(char*), then has to explicit cast "0" to "char*" as foo((char*)0). One workaround has been proposed by Meyer[1].
const  // this is a const object... 
class { 
public:   
      template<class T>  // convertible to any type     
      operator T*() const  // of null non-member     
      { 
              return 0; 
      }  // pointer...  
      template<class C, class T> // or any type of null     
      operator T C::*() const  // member pointer...     
      { 
               return 0; 
      }  
private:   
      void operator&() const;  // whose address can't be taken 
} nullptr = {};  // and whose name is nullptr

And Sutter and Stroustrup have clearly pointed out the advantages and disadvantages of Meyer's workaround in [2]. The main advantage is that it is a user-defined class and it will not break any code. And the main disadvantages are that can hide other definition of nullptr, can't implicitly convert to bool type, and compile only when adding this head file.

Note: "NULL" is a hash/macro definition in C++03. Simply it is equivalent to 0.

2 C++11 solution - nullptr
nullptr is introduced in C++11 as a keyword to distinguish null pointer constant from integer 0/NULL. Features:
- Can be implicitly converted into null pointer of any type
- Can not be implicitly converted into any integer type

Here is the list testings on Miscrosoft C++ 2010 Express
*********************************************************************************
void foo(int i)
{
    std::cout << "foo(int)" << std::endl;
}

void foo(char* chPtr)
{
    std::cout << "foo(char*)" << std::endl;
}

foo(0);              // foo(int)
foo((char*)0);              // foo(char*)
foo(NULL);                  // foo(int)
foo((char*)NULL);      // foo(char*)
foo(1);                      // foo(int)
foo(true);                       // foo(int)
foo((char*)1);                // foo(char*);
foo((char*)true);            // foo(char*);
foo(false);                      // foo(int);
foo(nullptr);                    // foo(char*)
*********************************************************************************
void foo(char* chPtr)
{
    std::cout << "foo(char*)" << std::endl;
}

foo(0);                       // error
foo((char*)0);               // ok
foo(NULL);                   // error
foo((char*)NULL);        // ok
foo(1);                       // error
foo(true);                        // error
foo((char*)1);                 // ok
foo((char*)true);             // ok
foo(false);                       // ok, foo(char*)
foo(nullptr);                     // ok
*********************************************************************************
void foo(int i)
{
    std::cout << "foo(int)" << std::endl;
}

foo(0);                       // foo(int)
foo((char*)0);                // error
foo(NULL);               // foo(int)
foo((char*)NULL);        // error
foo(1);                      // foo(int)
foo(true);                       // foo(int)
foo((char*)1);                // error
foo((char*)true);            // error
foo(false);                      // ok, foo(int)
foo(nullptr);                   // error
*********************************************************************************
if (NULL)  //equivalent as if (0), evaluated as false
if (!NULL) // true
if (nullptr)   // error
if (!nullptr)  // error
if (nullptr == 0)         // ok evaluated as true
                                // supposed to be error according to HS's proposal [2]
if (NULL==nullptr) // ok evaluated as true
                                // supposed to be error according to HS's proposal [2]
*********************************************************************************
char* chPtr = NULL;
char* chPtr1= nullptr;
int* iPtr = NULL;
int* iPtr1 = nullptr;

char x = NULL;
char x1 = nullptr; //error
bool z = NULL;
bool z1 = nullptr; //ok evaluted as false

if (chPtr == iPtr)      // error - different type of null pointer
                               // can not be compared according to C++03
if (chPtr1 == iPtr1)  // error
if (chPtr == chPtr1) // ok

if (x == NULL)
if (x == nullptr) // error - can not convert into an integer

if (chPtr == NULL) {}
if (chPtr == nullptr) {}
if (chPtr1 == nullptr) {}
if (chPtr1 == NULL) {}
*********************************************************************************
size_t s = sizeof(nullptr); // return 4 in win32 bit version
typeid(nullptr);  // return type of nullptr_t
throw nullptr;

char* chPtr2 = 1? nullptr : nullptr;
char* chPtr3 = 1? NULL : nullptr; // ok, supposed to error according to HS's proposal
char* chPtr3 = 1? 0 : nullptr; // ok, supposed to error according to HS's proposal

int xx = 1? nullptr : nullptr; // error
int xxx = 1? NULL : nullptr; // error
*********************************************************************************
template<class T> void bar(T* t)
{
    std::cout << "Bar(T*)" << std::endl;
}

template<class T> void hel(T t)
{
    std::cout << "hel(T)" << std::endl;
}

bar(nullptr);// error
hel(nullptr); // nullptr_t
bar((int*)nullptr);
hel((int*)nullptr);

These are tests done so far. Most of the implementation in VC10 is already aligned with C+11.


Bibliography:
[1]  S. Meyers. More Effective C++, 2nd edition (Addison-Wesley, 1996).
[2]  Herb Sutter and Bjarne Stroustrup: A name for the null pointer: nullptr (revision 4) .[N2214 = 07-0074 ]
[3] http://www.stroustrup.com/C++11FAQ.html#nullptr
[4] http://en.wikipedia.org/wiki/C++11

No comments:

Post a Comment