C++コアガイドライン が最近発表されました(おめでとうございます!)gsl::not_null
タイプが心配です。 I.12で述べたように、not_null
としてnullであってはならないポインターを宣言します。
Nullptrエラーの逆参照を回避するため。 nullptrの冗長チェックを回避することにより、パフォーマンスを改善します。
...
ソースで意図を述べることにより、実装者とツールは、静的分析を通じていくつかのクラスのエラーを見つけるなどのより良い診断を提供し、ブランチやnullテストの削除などの最適化を実行できます。
意図は明確です。ただし、そのための言語機能は既にあります。 nullにできないポインターは参照と呼ばれます。また、参照は一度作成されるとリバウンドできませんが、この問題はstd::reference_wrapper
によって解決されます。
gsl::not_null
とstd::reference_wrapper
の主な違いは、後者はポインターの代わりにしか使用できないのに対し、前者はnullptr
- assignable( F.17:not_nullを使用して、「null」が有効な値ではないことを示します ):
not_null
は、組み込みのポインター専用ではありません。array_view
、string_view
、unique_ptr
、shared_ptr
、およびその他のポインターのようなタイプで機能します。
次のような機能比較表を想像します。
T&
:
nullptr
を保存できませんか? -はいstd::reference_wrapper<T>
:
nullptr
を保存できませんか? -はいgsl::not_null<T*>
:
nullptr
を保存できませんか? -はい最後に質問です。
std::reference_wrapper
が今では役に立たないということですか?[〜#〜] ps [〜#〜]このためにcpp-core-guidelines
およびguideline-support-library
というタグを作成しました。
参照はnot nullにできないポインターです。参照は意味的にポインタとは大きく異なります。
参照には、値割り当てと比較のセマンティクスがあります。つまり、参照を含む割り当てまたは比較操作は、参照されたvalueの読み取りと書き込みを行います。ポインターには(逆に)参照割り当てと比較のセマンティクスがあります。つまり、ポインターを含む割り当てまたは比較操作では、reference自体(つまり、参照されるオブジェクトのアドレス)の読み取りと書き込みを行います。
ご指摘のとおり、参照は(値の割り当てのセマンティクスにより)リバウンドできませんが、reference_wrapper<T>
クラステンプレートcanリバウンド。これはreference割り当てセマンティクスを持つためです。それの訳は reference_wrapper<T>
は、STLコンテナおよびアルゴリズムで使用するように設計されており、コピー割り当て演算子がコピーコンストラクターと同じことを行わなかった場合、正しく動作しません。しかしながら、 reference_wrapper<T>
はまだ値を持っています比較参照のようなセマンティクスなので、STLコンテナーとアルゴリズムで使用される場合、ポインターとは非常に異なる動作をします。例えば、 set<T*>
には、同じ値を持つ異なるオブジェクトへのポインタを含めることができますが、set<reference_wrapper<T>>
には、特定の値を持つ1つのオブジェクトのみへの参照を含めることができます。
not_null<T*>
クラステンプレートには、参照割り当てがありますandポインタのような比較セマンティクス。これはポインターのような型です。これは、STLコンテナーおよびアルゴリズムで使用される場合、ポインターのように動作することを意味します。 nullにすることはできません。
そのため、比較のセマンティクスを忘れた場合を除き、評価は正しいです。いいえ、reference_wrapper<T>
は、参照のような値比較のセマンティクスを持っているため、どのようなポインターのような型によっても廃止されません。
std::reference_wrapper
でカバーされていないgsl::not_null
のユースケースはまだあると思います。基本的に、std::reference_wrapper
は参照をミラーリングしてoperator T&
変換を行い、not_null
はoperator->
とのポインターインターフェースを持ちます。すぐに頭に浮かぶユースケースの1つは、スレッドを作成する場合です。
void funcWithReference(int& x) { x = 42; }
int i=0;
auto t = std::thread( funcWithReference, std::ref(i) );
funcWithReference
を制御できない場合、not_null
を使用できません。
同じことがアルゴリズムのファンクターにも当てはまり、boost::signals
のバインドにも使用しなければなりませんでした。