[英]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.