はい、ポイントstd::observer_ptr
は主に「自己文書化」であり、それ自体が有効な目的です。しかし、「オブザーバー」ポインターが何であるかが正確には明らかではないため、おそらくそれはうまく機能しないことを指摘する必要があります。まず、Galik が指摘するように、一部の人にとっては、名前はターゲットを変更しないというコミットメントを暗示しているように見えますが、これは意図ではありませんaccess_ptr
。そして第二に、修飾子がなければ、名前はその「非機能的」動作の承認を暗示します. たとえば、std::weak_ptr
を「オブザーバー」ポインターのタイプと見なすことができます。ただしstd::weak_ptr
、(割り当て解除された) オブジェクトへのアクセス試行を安全に失敗させるメカニズムを提供することにより、ポインターがターゲット オブジェクトよりも長く存続する場合に対応します。std::observer_ptr
の実装はこのケースに対応していません。raw_access_ptr
したがって、機能上の欠点をより適切に示すため、おそらくより適切な名前になります。
では、当然のことながら、この機能的に問題のある「非所有」ポインターのポイントは何ですか? 主な理由はおそらくパフォーマンスです。多くの C++ プログラマーは、 のオーバーヘッドがstd::share_ptr
高すぎると認識しているため、「オブザーバー」ポインターが必要な場合はそのまま生ポインターを使用します。提案されたstd::observer_ptr
試みは、許容できるパフォーマンス コストでコードの明瞭さをわずかに改善することを目的としています。具体的には、パフォーマンス コストはゼロです。
残念ながら、生のポインターを「オブザーバー」ポインターとして使用することがどれだけ安全かについて、非現実的な楽観論が広まっているようですが、私の意見ではそうです。特に、ターゲット オブジェクトが より長く存続しなければならないという要件を述べるのは簡単ですがstd::observer_ptr
、それが満たされていることを完全に確信することは必ずしも容易ではありません。次の例を検討してください。
struct employee_t {
employee_t(const std::string& first_name, const std::string& last_name) : m_first_name(first_name), m_last_name(last_name) {}
std::string m_first_name;
std::string m_last_name;
};
void replace_last_employee_with(const std::observer_ptr<employee_t> p_new_employee, std::list<employee_t>& employee_list) {
if (1 <= employee_list.size()) {
employee_list.pop_back();
}
employee_list.push_back(*p_new_employee);
}
void main(int argc, char* argv[]) {
std::list<employee_t> current_employee_list;
current_employee_list.push_back(employee_t("Julie", "Jones"));
current_employee_list.push_back(employee_t("John", "Smith"));
std::observer_ptr<employee_t> p_person_who_convinces_boss_to_rehire_him(&(current_employee_list.back()));
replace_last_employee_with(p_person_who_convinces_boss_to_rehire_him, current_employee_list);
}
関数の作成者はreplace_last_employee_with()
、新規採用者への参照が、置き換えられる既存の従業員への参照である可能性があることに思いもよらなかった可能性があります。この場合、関数がstd::observer_ptr<employee_t>
終了する前に、パラメーターのターゲットが誤って割り当て解除される可能性があります。それを使用しています。
これは不自然な例ですが、この種のことは、より複雑な状況で簡単に発生する可能性があります。もちろん、ほとんどの場合、生のポインターを使用しても完全に安全です。問題は、実際には安全ではないのに、安全であると簡単に思い込んでしまう少数のケースがあることです。
std::observer_ptr<employee_t>
パラメータをstd::shared_ptr
orに置き換えることstd::weak_ptr
が何らかの理由で受け入れられない場合、別の安全なオプションがあります. 「登録済みポインター」はnull_ptr
、ターゲット オブジェクトが破棄されたときに (自動的に) 設定されることを除いて、生のポインターと同じように動作するスマート ポインターであり、既に削除されているオブジェクトにアクセスしようとすると、既定で例外がスローされます。 . それらは一般的に高速ですstd::shared_ptrs よりも優れていますが、パフォーマンスの要求が非常に厳しい場合は、登録されたポインターをコンパイル時のディレクティブで "無効" (対応する生のポインターに自動的に置き換える) ことができ、デバッグで使用できる (そしてオーバーヘッドが発生する) ことができます。 /test/beta モードのみ。
したがって、生のポインターに基づく「オブザーバー」ポインターが存在する場合は、おそらく、登録されたポインターに基づくポインターが存在し、おそらく OP が示唆したように、std::shared_ptr に基づくポインターも存在するはずです。