最初に、C++プライマーが_unique_ptr
_および_shared_ptr
_について言ったことを見てください。
$ 16.1.6。効率と柔軟性
_
shared_ptr
_は、削除者のタイプが実行時までわからないため、削除者を直接のメンバーとして保持しないことを確認できます。.削除者の型は_
unique_ptr
_の型の一部であるため、削除者メンバーの型はコンパイル時に認識されます。削除者は各_unique_ptr
_オブジェクトに直接保存できます。
したがって、_shared_ptr
_にはdeleterの直接のメンバーはないように見えますが、_unique_ptr
_にはあります。ただし、 別の質問のトップ投票の回答 は次のとおりです。
テンプレート引数として削除プログラムを提供する場合(_
unique_ptr
_のように)、それはタイプの一部であり、このタイプのオブジェクトに追加のものを保存する必要はありません。 deleterがコンストラクターの引数として渡される場合(_shared_ptr
_のように)オブジェクトに保存する必要があります。これは、同じタイプのオブジェクトに異なる削除プログラムを使用できるため、柔軟性が追加されます。
引用された2つの段落は完全に矛盾しているため、混乱しています。さらに、 多くの人は_unique_ptr
_はオーバーヘッドがゼロだと言います 削除者をメンバーとして保存する必要がないためです。ただし、ご存知のように、_unique_ptr
_にはunique_ptr<obj,del> p(new obj,fcn)
のコンストラクターがあります。つまり、削除者を渡すことができるため、_unique_ptr
_は削除者をメンバーとして保存しているようです。なんてこった!
std::unique_ptr<T>
は、オーバーヘッドがゼロになる可能性が非常に高くなります(正常な標準ライブラリの実装で)。 std::unique_ptr<T, D>
は、任意のD
の場合、一般的にゼロオーバーヘッドではありません。
理由は簡単です。空の(つまり、ステートレスな)タイプ(std::default_delete
インスタンス化など)の場合、空のベースの最適化を使用して削除者のストレージを削除できます。
あなたを混乱させるようなキーフレーズは、「削除者canを直接保存する」です。ただし、std::default_delete
型の削除機能を保存しても意味がありません。必要な場合は、std::default_delete{}
として作成できます。
一般に、ステートレス削除機能は必要に応じて作成できるため、保存する必要はありません。
簡単な紹介:
unique_ptr canいくつかの小さなオーバーヘッドを導入しますが、削除者のためではありませんが、そこから移動するときに値をnullに設定する必要があるため、生のポインタを使用している場合はバグを起こしやすい古いポインタを残すことができますしかし、それが以前に指し示していた場所をまだ指している正当な状態。スマートオプティマイザーは明らかに最適化できますが、保証はされません。
削除者に戻る:
他の答えは正しいが、精巧である。 EBOまたはその他の複雑な用語の言及を除いた簡易バージョンです。
Deleterが空の場合(状態がない場合)、unique_ptr内に保持する必要はありません。必要な場合は、必要なときに構築できます。知っておく必要があるのは削除タイプのみです(これはunique_ptrのテンプレート引数の1つです)。
たとえば、次のコードを検討してください。また、ステートレスオブジェクトのオンデマンドでの簡単な作成も示しています。
#include <iostream>
#include <string>
#include <string_view>
template<typename Person>
struct Greeter{
void greet(){
static_assert(std::is_empty_v<Person>, "Person must be stateless");
Person p; // Stateless Person instance constructed on demand
std::cout << "Hello " << p() << std::endl;
}
// ... and not kept as a member.
};
struct Bjarne{
std::string_view operator()(){
return "Bjarne";
}
};
int main() {
Greeter<Bjarne> hello_bjarne;
hello_bjarne.greet();
}