C++11- nullptr != NULL

C++11 standartlarıyla beraber C++ programlama dili yavaş yavaş daha deterministik olmaya başladı. Yani belirsiz durumlar, kullanım hataları dilin standartlarının desteğiyle en aza çekiliyor. Bu kapsamda nullptr de uzun zamandır bilinen bir sorunu çözüyor.

#include <iostream>
#include <memory>


using namespace std;



void func(int x) 
{ 
    cout << "int" << endl; 
}

void func(char *y) 
{
    cout << "pointer" << endl; 
}
 
int main() {
    func(0);        // int overload
    func((char *)0); // char * overload
    func(nullptr);  // char* overload
    func(NULL);     // main.cpp:23:14: error: call of overloaded ‘func(NULL)’ is ambiguous (compiler: g++5)
}

Üstteki hata aslında derleyiciden derleyiciye farklı olabilir, örneğin visual studio da ‘int’ olarak yorumlanabilir. Kısacası non-deterministic bir durum sözkonusu, derleyicinden derleyiciye farkediyor.
Aslında sorun C++ programlama dilinin iki temel özelliğinden kaynaklanıyor; Function Overloading ve Otomatik Tür dönüşümü. Şimdi bir de NULL isminin nasıl tanımlandığına bakalım:

#if defined (_STDDEF_H) || defined (__need_NULL)
#undef NULL		/* in case <stdio.h> has defined it. */
#ifdef __GNUG__
#define NULL __null
#else   /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0)
#else   /* C++ */
#define NULL 0
#endif  /* C++ */
#endif  /* G++ */
#endif	/* NULL not defined and <stddef.h> or need NULL.  */
#undef	__need_NULL

Görüldüğü üzere derleyiciniz cplusplus derleyicisi ise NULL ismi 0 olarak tanımlanmış. İşte tam da burda sıkıntı ortaya çıkıyor.

Derleyici ‘func(NULL)’ dediğinizde hangi fonksiyonu çağıracağını bilmiyor(g++5 için) ya da kullandığınız başka bir derleyici de farklı bir davranış gözlemleyebilirsiniz integer veya char * function overloadlarından biri çağırılabilir. Hülasası başta da belirttiğim gibi C++11 bu tarz non-deterministic konulara da el atıyor ve bu meseleyi nullptr ile çözüyor. Artık NULL yazdığınız her yere nullptr yazarak daha güvenli programlama yapmış olabilirsiniz özellikle cross-platform yazılım geliştiyorsanız.

nullptr’ nin aslında türü std::nullptr_t ve bir prvalue’ dur. Dolayısıyla herhangi bir şekilde deference edilemez.

Bunun yanı sıra nullptr meta programmingte perfect forwardinge de olanak sağlamaktadır. Şöyle ki:

#include <iostream>
#include <memory>


using namespace std;


template<class F, class A>
void foo(F f, A a)
{
    f(a);
}
 

void func(char *y) 
{
    cout << "pointer" << endl; 
}
 
int main() {
    func(0);        // int overload
    func((char *)0); // void * overload
    func(nullptr);  // void * overload
    
    //foo(func, NULL); //error: invalid conversion from ‘long int’ to ‘char*’ [-fpermissive]
    foo(func, nullptr);   
}

foo şablon fonksiyonunun ikinci parametresine NULL verirsek derleyici f fonksiyonunu integer bir değerle çağırmaya çalışacak. Çünkü şablon parametresi olarak NULL ‘ ın türü integer tespit edildi. nullptr ile çağırdığımızda ise türü std::nullptr_t olarak tespit edileceğinden sorunsuzca ‘f’ veya ‘func’ fonksiyonu sorunsuzca çağırılacaktır.

Aslında nullptr nin implementasyonu bir const class’dır. Merak edenler için standart taslağında implementasyonu şu şekildedir:


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 = {};

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s