nullptrの基本
std::nullptr_t
nullポインタリテラルnullptrの型です。タイプのprvalue / rvalue std::nullptr_t
です。nullptrから任意のポインター型のnullポインター値への暗黙的な変換が存在します。
リテラル0は、ポインターではなくintです。C ++が、ポインターのみを使用できるコンテキストで0を見ていることに気付いた場合、それは0をnullポインターとして不当に解釈しますが、それはフォールバック位置です。C ++の主なポリシーは、0はポインターではなくintであることです。
利点1-ポインター型および整数型のオーバーロード時のあいまいさを取り除く
C ++ 98では、これの主な意味は、ポインターおよび整数型のオーバーロードが予期しない結果を招く可能性があることでした。このようなオーバーロードに0またはNULLを渡しても、ポインターオーバーロードは呼び出されませんでした。
void fun(int); // two overloads of fun
void fun(void*);
fun(0); // calls f(int), not fun(void*)
fun(NULL); // might not compile, but typically calls fun(int). Never calls fun(void*)
この呼び出しの興味深い点は、ソースコードの明らかな意味(「私はNULLをnullポインタでfunを呼び出しています」)と実際の意味(「nullではなく、何らかの整数でfunを呼び出しています。ポインタ」)。
nullptrの利点は、整数型がないことです。nullptrは整数と見なすことができないため、nullptrでオーバーロードされた関数funを呼び出すと、void *オーバーロード(つまり、ポインターオーバーロード)が呼び出されます。
fun(nullptr); // calls fun(void*) overload
したがって、0またはNULLの代わりにnullptrを使用すると、オーバーロードの解決の驚きを回避できます。
戻り値の型にautoを使用nullptr
するNULL(0)
場合のもう1つの利点
たとえば、コードベースでこれが発生したとします。
auto result = findRecord( /* arguments */ );
if (result == 0) {
....
}
findRecordが返すものをたまたま知っていない(または簡単に見つけられない)場合、結果がポインター型か整数型かが明確ではない可能性があります。結局のところ、0(どの結果に対してテストされるか)はどちらの方向にも進む可能性があります。一方、次のような場合、
auto result = findRecord( /* arguments */ );
if (result == nullptr) {
...
}
あいまいさはありません。結果はポインタ型でなければなりません。
メリット3
#include<iostream>
#include <memory>
#include <thread>
#include <mutex>
using namespace std;
int f1(std::shared_ptr<int> spw) // call these only when
{
//do something
return 0;
}
double f2(std::unique_ptr<int> upw) // the appropriate
{
//do something
return 0.0;
}
bool f3(int* pw) // mutex is locked
{
return 0;
}
std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
using MuxtexGuard = std::lock_guard<std::mutex>;
void lockAndCallF1()
{
MuxtexGuard g(f1m); // lock mutex for f1
auto result = f1(static_cast<int>(0)); // pass 0 as null ptr to f1
cout<< result<<endl;
}
void lockAndCallF2()
{
MuxtexGuard g(f2m); // lock mutex for f2
auto result = f2(static_cast<int>(NULL)); // pass NULL as null ptr to f2
cout<< result<<endl;
}
void lockAndCallF3()
{
MuxtexGuard g(f3m); // lock mutex for f2
auto result = f3(nullptr);// pass nullptr as null ptr to f3
cout<< result<<endl;
} // unlock mutex
int main()
{
lockAndCallF1();
lockAndCallF2();
lockAndCallF3();
return 0;
}
上記のプログラムは正常にコンパイルおよび実行されましたが、lockAndCallF1、lockAndCallF2およびlockAndCallF3には冗長なコードがあります。これらすべてのテンプレートを記述できる場合、このようなコードを記述するのは残念lockAndCallF1, lockAndCallF2 & lockAndCallF3
です。したがって、テンプレートで一般化することができます。冗長なコードlockAndCall
を複数定義する代わりに、テンプレート関数をlockAndCallF1, lockAndCallF2 & lockAndCallF3
作成しました。
コードは次のようにリファクタリングされます。
#include<iostream>
#include <memory>
#include <thread>
#include <mutex>
using namespace std;
int f1(std::shared_ptr<int> spw) // call these only when
{
//do something
return 0;
}
double f2(std::unique_ptr<int> upw) // the appropriate
{
//do something
return 0.0;
}
bool f3(int* pw) // mutex is locked
{
return 0;
}
std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
using MuxtexGuard = std::lock_guard<std::mutex>;
template<typename FuncType, typename MuxType, typename PtrType>
auto lockAndCall(FuncType func, MuxType& mutex, PtrType ptr) -> decltype(func(ptr))
//decltype(auto) lockAndCall(FuncType func, MuxType& mutex, PtrType ptr)
{
MuxtexGuard g(mutex);
return func(ptr);
}
int main()
{
auto result1 = lockAndCall(f1, f1m, 0); //compilation failed
//do something
auto result2 = lockAndCall(f2, f2m, NULL); //compilation failed
//do something
auto result3 = lockAndCall(f3, f3m, nullptr);
//do something
return 0;
}
コンパイルがために失敗した理由を詳細分析のlockAndCall(f1, f1m, 0) & lockAndCall(f3, f3m, nullptr)
ためではありませんlockAndCall(f3, f3m, nullptr)
なぜコンパイルがlockAndCall(f1, f1m, 0) & lockAndCall(f3, f3m, nullptr)
失敗したのですか?
問題は、lockAndCallに0が渡されると、テンプレートタイプの推論が始まり、そのタイプを理解することです。タイプ0はintなので、lockAndCallへのこの呼び出しのインスタンス化内のパラメーターptrのタイプです。残念ながら、これは、lockAndCall内のfuncの呼び出しで、intが渡されていることを意味しstd::shared_ptr<int>
ますf1
。これは、予期するパラメーターと互換性がありません。への呼び出しで渡された0はlockAndCall
、nullポインタを表すためのものでしたが、実際に渡されたのはintでした。このintをstd::shared_ptr<int>
タイプエラーとしてf1に渡そうとすると、lockAndCall
テンプレート内で、intがを必要とする関数に渡されているため、with 0 の呼び出しは失敗しますstd::shared_ptr<int>
。
関係する呼び出しの分析NULL
は基本的に同じです。ときNULL
に渡されたlockAndCall
、一体型は、パラメータPTRのために推定され、時に型エラーが発生しptr
-an intまたはint型のように渡されたタイプが-されf2
得ることを期待され、std::unique_ptr<int>
。
対照的に、通話は問題nullptr
ありません。ときnullptr
に渡されlockAndCall
、用型がptr
あることを推測されますstd::nullptr_t
。ときptr
に渡されるf3
、からの暗黙の型変換がありますstd::nullptr_t
にはint*
あるため、std::nullptr_t
すべてのポインタ型に暗黙的に変換します。
ヌルポインターを参照する場合は常に、0やではなくnullptrを使用することをお勧めしますNULL
。
int
とはvoid *
選択しません。int
オーバーバージョンvoid *
使用してバージョンをnullptr
。