8

次のようなクロスプラットフォーム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でコンパイルしたものです。一時ファイルの有効期間を延長する必要がある可能性がありますが、コンパイラにバグがある可能性があります。

4

2 に答える 2

7

できることはas_utf8()、左辺値で使用する場合と右辺値で使用する場合の 2 つの異なるバージョンを作成することです。ただし、C++11 が必要です。

const&そうすれば、オブジェクトが一時的なものではない場合と、そうでない場合の効率的な移動の両方の世界を最大限に活用できます。

std::string const& as_utf8() const & {
                               // ^^^ Called from lvalues only
    return path;
}

std::string as_utf8() const && {
                        // ^^^^ Called from rvalues only
    return std::move(path); //We don't need path any more
}
于 2016-08-24T16:14:47.780 に答える
1

私の考えでは、参照を返すかオブジェクトを返すかに関する指針となる原則は、元のクラスの定義された役割を調べることです。

つまり、単純なプロパティを公開しているメソッドですか (特に不変の場合は参照を主張します)、それとも何かを生成していますか?

新しいオブジェクトまたは表現を生成している場合は、別のオブジェクトを返すことが合理的に期待できます。

一般に、API のユーザーは、プロパティがホスト オブジェクトより長く存続しないことを理解することに慣れています。もちろん、これはドキュメントで明確にすることができます。

例えば

struct path
{
    /// a property
    /// @note lifetime is no longer than the lifetime of this object
    std::string const& native() const;

    /// generate a new string representation in a different format
    std::string to_url() const;

};

個人的には、この場合の接頭辞は避けas_ます。これは、次のような同じオブジェクトの新しい表現を返すことを示唆しているためです。

struct world 
: std::enable_shared_from_this<world>
{
    struct sky {} my_sky_;

    /// returns a shared_ptr to my sky object, which shares its lifetime
    /// with this world.
    std::shared_ptr<sky> as_sky() 
    { 
      return std::shared_ptr<sky>(shared_from_this(), std::addressof(my_sky_));
    }
};
于 2016-08-24T16:36:48.487 に答える