簡體   English   中英

臨時數據成員和API設計的終身擴展

[英]Lifetime extension of temporaries' data members and API design

假設我有一個跨平台的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)的平台, as_utf8()將引用直接返回到內部表示path似乎是合理的,因為它已經是 UTF-8。

如果我有以下代碼:

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;
    }

然后代碼是正確的。 但是,每次調用此成員函數時創建臨時文件都是低效的。 言下之意也是沒有 “消氣”成員函數應該回到自己的數據成員的引用。

如果API保持原樣,那么它似乎給調用者帶來了不必要的負擔,必須查看as_utf8()的返回類型以查看它是否返回const& :如果是,則調用者必須使用一個對象而不是一個const& ; 如果它返回一個對象,那么調用者可以使用const&

那么有什么方法可以解決這個問題,這樣API在大多數情況下都是高效的,但卻阻止用戶從看似無害的代碼中獲取懸空引用?


順便說一句,這是使用g ++ 5.3編譯的。 這有可能是暫時的壽命應該延長,但是編譯器有一個錯誤。

你可以做的是創建2個不同版本的as_utf8() ,一個用於左值,一個用於右值。 但是你需要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
}

在我看來,關於是否返回引用或對象的指導原則是檢查原始類的定義角色。

ie是暴露一個簡單屬性的方法(爭論參考,特別是如果它是不可變的),還是生成一些東西?

如果它正在生成一個新的對象或表示,我們可以合理地期望它返回一個不同的對象。

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_));
    }
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM