如 cppreference 中所述:https ://en.cppreference.com/w/cpp/language/copy_elision
在 c 17 中保證在某些情況下必須應用復制省略,例如
SomeBigObject SomeBigObject::Factory(...) {
SomeBigObject local;
...
return local;
}
(示例取自https://abseil.io/tips/11)
但不適用于像
SomeBigObject SomeBigObject::Factory(...) {
SomeBigObject local1;
SomeBigObject local2;
...
if (cond_1) {
return local1;
} else {
return local2;
}
}
對于 C 用戶,要 100% 確定復制省略/NVO 應用于上述函式的回傳值有點困難。
所以,為了確保一個物件不會被復制,我們通常寫這樣的東西
SomeBigObject obj;
func(&obj);
而在大多數情況下,像
SomeBigObject obj = func();
就足夠了。
是否有任何語言/編譯器工具可以幫助我們在編譯時保證這一點(也許是某種 static_assert()),以便我們可以自信地撰寫這些單行代碼?
我知道將復制建構式標記為洗掉會有好處,但在某些情況下需要復制。
uj5u.com熱心網友回復:
在 c 17 中保證在某些情況下必須應用復制省略,例如
SomeBigObject SomeBigObject::Factory(...) { SomeBigObject local; ... return local; }
那是不正確的。保證復制省略僅適用于在回傳陳述句中創建回傳物件的情況,例如
SomeBigObject SomeBigObject::Factory(...) {
// code
return SomeBigObject(some_intializer);
// or
return {some_intializer};
}
您問題中的兩個代碼塊都是 NRVO(命名回傳值優化)的示例,并且不能保證會應用 NRVO,只是允許應用它。
如果您的物件是可移動的,并且移動是一種廉價操作,那么您無需執行任何操作,因為所有函式區域物件在 return 陳述句中都被視為右值并在可能的情況下移動。
如果您的物件沒有便宜的移動操作,那么您可以做的下一個避免昂貴副本的最佳方法是使用智能指標,如
std::unique_ptr<SomeBigObject> SomeBigObject::Factory(...) {
auto local = std::make_unique<SomeBigObject>();
// code
return local;
}
回傳的所有成本都是指標交換。
uj5u.com熱心網友回復:
您的第一個示例也不保證復制省略。第一個和第二個示例都可以省略副本,但這取決于編譯器。然而,在第一種情況下,編譯器識別模式并應用優化要簡單得多。所以你很可能會看到它在第一種情況下應用,但在第二種情況下不太可能,因為編譯器可能無法跟蹤應該將兩個區域物件中的哪一個構造為結果物件,盡管它仍然可能視具體情況而定。
使用
SomeBigObject obj;
func(&obj);
form 也沒有解決這個問題。函式引數可以很容易地用兩個localX物件中的一個來識別,這正是為什么編譯器通常更難對這種情況進行優化的原因。
另請注意,您鏈接的文章是在 C 17 之前撰寫的,當時某些復制省略首次成??為強制性要求。在 C 17 之前,一切都取決于編譯器。這篇文章只是反映了典型實作的行為(當時?)。
現在的規則非常簡單:如果您從相同型別的純右值(直到 cv 限定符)初始化一個物件,那么復制省略是有保證的。這適用于通過多個純右值進行的鏈式初始化,也適用于函式呼叫。
func()是型別的純右值SomeBigObject,因此在SomeBigObject obj = func();復制中省略是強制性的,因為沒有物件作為函式的回傳值不同于obj。(C 17 起)
然而,在您的兩個函式實作中,return運算元都是左值,因此不能保證objand local/localX將彼此標識。在此處滿足的某些情況下,編譯器可能仍會進行此識別并消除復制/移動。但這始終是非強制性的。
所以理想情況下,您不希望回傳區域變數,而是return在函式邏輯允許的情況下直接在陳述句中構造回傳值。或者至少嘗試限定用作return運算元的每個區域變數的范圍,以便return其范圍內的所有陳述句僅回傳該變數。
我不知道有任何工具可用于通用編譯器或在編譯時靜態檢查非強制命名回傳值優化是否應用于函式的語言。
uj5u.com熱心網友回復:
不管是否保證復制省略,如果你真的需要保證不呼叫復制建構式,你可以使用如下包裝類:
template<typename T>
struct NoCopy
{
NoCopy() = default;
template<typename ...Args>
NoCopy(Args&&... args):
value(std::forward<Args>(args)...)
{}
NoCopy(NoCopy&&) = default;
NoCopy(NoCopy&) = delete;
NoCopy(const NoCopy&) = delete;
T value;
};
包裝器不能被復制,但它的成員可以
NoCopy<SomeBigObject> obj(arguments...);
auto cpy = obj; //error
auto mv = std::move(obj); // OK
auto cpyMember = obj.value; // OK
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/534082.html
標籤:C C 17
