web-dev-qa-db-ja.com

削除者を保存する必要がある場合、unique_ptrにオーバーヘッドを持たない方法を教えてください。

最初に、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_は削除者をメンバーとして保存しているようです。なんてこった!

26
bigxiao

std::unique_ptr<T>は、オーバーヘッドがゼロになる可能性が非常に高くなります(正常な標準ライブラリの実装で)。 std::unique_ptr<T, D>は、任意のDの場合、一般的にゼロオーバーヘッドではありません。

理由は簡単です。空の(つまり、ステートレスな)タイプ(std::default_deleteインスタンス化など)の場合、空のベースの最適化を使用して削除者のストレージを削除できます。

31
Angew

あなたを混乱させるようなキーフレーズは、「削除者canを直接保存する」です。ただし、std::default_delete型の削除機能を保存しても意味がありません。必要な場合は、std::default_delete{}として作成できます。

一般に、ステートレス削除機能は必要に応じて作成できるため、保存する必要はありません。

12
MSalters

Angewの答え 何が起こっているかをかなり徹底的に説明した。

カバーの下で物事がどのように見えるかを知りたい人のために

template<typename T, typename D, bool Empty = std::is_empty_v<D>>
class unique_ptr
{
    T* ptr;
    D d;

    // ... 
};

template<typename T, typename D>
class unique_ptr<T, D, true> : D
{
    T* ptr;

    // ...
};

空の削除者に特化し、 空のベース最適化 を利用します。

10
Passer By

簡単な紹介:

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();
}
0
NoSenseEtAl