次のようなクロスプラットフォームPath
クラスがあるとします。
class Path {
public:
// ...
Path parent() const; // e.g., /foo/bar -> /foo
std::string const& as_utf8() const {
return path;
}
private:
std::string path;
};
メンバー関数はパスのparent()
親パスを返すthis
ため、(正しく) それを表す新しく構築されたPath
オブジェクトを返します。
OS レベルでパスを UTF-8 文字列として表すプラットフォーム (Unix など) の場合、内部表現はすでにUTF-8であるためas_utf8()
、内部表現への参照を直接返すことが合理的と思われます。path
次のようなコードがある場合:
std::string const &s = my_path.as_utf8(); // OK (as long as my_path exists)
// ...
Path const &parent = my_path.parent(); // OK (temporary lifetime extended)
次の理由により、これらの行はどちらも問題ありません。
my_path
持続すると仮定すると、s
有効なままになります。- によって返される一時オブジェクトの有効期間は、によって
parent()
延長されますconst&
。
ここまでは順調ですね。ただし、次のようなコードがある場合:
std::string const &s = my_path.parent().as_utf8(); // WRONG
が一時オブジェクトを参照するのではなく、そのデータ メンバーを参照するため、によって返される一時オブジェクトの有効期間が延長されないため、これは誤りです。この時点で を使用しようとすると、ガベージまたはコア ダンプが発生します。コードが代わりにあった場合:parent()
const&
s
std::string as_utf8() const { // Note: object and NOT const&
return path;
}
その場合、コードは正しいでしょう。ただし、このメンバー関数が呼び出されるたびにテンポラリを作成するのは効率的ではありません。また、 「getter」メンバー関数がデータ メンバーへの参照を返してはならないことも意味しています。
as_utf8()
API をそのままにしておくと、呼び出し元が a を返すかどうかを確認するためにの戻り値の型を調べなければならないという過度の負担がかかるように思われます。そうであるconst&
場合、呼び出し元はオブジェクトを使用する必要があります。ではありませんconst&
。オブジェクトを返す場合、呼び出し元はconst&
.
この問題を解決して、ほとんどの場合に API が効率的でありながら、一見無害に見えるコードからダングリング参照をユーザーが取得できないようにする方法はありますか?
ちなみにこれはg++ 5.3でコンパイルしたものです。一時ファイルの有効期間を延長する必要がある可能性がありますが、コンパイラにバグがある可能性があります。