C++で文字列を返すメンバ関数のベストプラクティスについて
C++でメンバ関数 (getter) から文字列値を返したい場合、その返り値の型はおおよそ次の3つに分類できるかと思います。
std::string
std::string getString() const { return m_member; }
新しく
std::string
の実体を生成するパターンです。
例えばstd::filesystem::path::*string()
はこのパターンにあたります。参照 (
const char*
,std::string_view
,const std::string&
など)std::string_view getStringRef() const { return m_member; }
メンバ変数の参照を何らかの形で表現した型を返すパターンです。
LLVMのAPIにはこのパターンが見られます。スマートポインタ (
std::shared_ptr<const std::string>
など)std::shared_ptr<const std::string> getStringPtr() const { return { shared_from_this(), &m_member }; }
メンバ変数のスマートポインタを返すパターンです。
参照カウントはthisと共有されます。
これが用いられているのを目にしたことはありません。
コード全文: https://wandbox.org/permlink/xWWJWpAbCAN9UWH2
それぞれに考えられるメリット、デメリットを挙げます。
std::string
メリット: 参照のライフタイムなどの余計な考慮が必要なくなる。
デメリット: 十分長い文字列に対して呼び出しごとにコピーのコストがかかる。(RVOが有効な場合でも1回のコピーが必要。)参照
メリット: 呼び出しごとのコピーが必要ない。
デメリット: 返り値の参照の有効範囲を実装から見極める必要がある。参照先の変更に影響される。内部にstd::string
を直接的/間接的に実体として持つことを要求する。スマートポインタ
メリット: 参照の有効範囲の考慮が必要ない。
デメリット: 参照先の変更に影響される。(例の場合)クラスがshared_ptr
によって管理されることを要求する。
普通であれば1.のようにstd::string
を返してしまえばよいと思うのですが、
次のような条件を考える場合には (あるいはそれ以外の場合についても) どのパターンがふさわしいか意見をお聞かせください。
条件:
- C++11以降である。
- SSOを考慮しない。(十分長い文字列も入りうる。)
- RVOを期待できる。
- 対象のメンバは他のメンバ関数などによって値を変更されることはない。
- 頻繁に呼ばれる可能性がある。