[英]Default move construction
《 C ++編程語言 》一書提供了有關默認操作的示例。 我的問題集中在默認移動操作上。
struct S {
std::string a;
int b;
};
S f(S arg) {
S s0{};
S s1(s0); //s1 {s0}; in the book
s1 = arg;
return s1;
}
之后它說:
s1的副本結構復制了s0.a和s0.b。 s1的返回將移動s1.a和s1.b,將s1.a保留為空字符串,而s1.b保持不變。 請注意,內置類型的移出對象的值未更改。 那是編譯器要做的最簡單,最快的事情。
我認為這意味着如果我寫:
S s3{"tool",42};
f(s3);
由於s1的值已移動,因此s1.a將返回到“”,而s1.b保持不變? 然后,當f()完成執行時,s1將被銷毀嗎? 我正在嘗試找到一種方法來測試我的猜測,但是在函數執行后我無法找到一種方法來了解s1的值,因為它當然是局部的。 我寫了一個析構函數,只是為了在值被銷毀之前找到它們。
~S() {
std::cout << a << " " << b << '\n';
}
輸出為:
0 //values of s0?
tool 42
tool 42
tool 42
看來我的猜測是完全錯誤的,我完全不理解本文。 誰能解釋引號中的文字?
s1.a
將返回到""
也許。 根據move構造函數的規范,它“處於未指定值的有效狀態”。 通常,如果字符串使用動態分配的數組,並且兩個字符串都使用兼容的分配器,則此舉將轉移該數組的所有權,而將舊字符串保留為空。 但是,如果字符串使用“短字符串優化”,即在string
對象本身內部存儲短字符串,以節省動態內存分配的成本,則保留舊字符串不變的速度會更快。
並且
s1.b
不變
是。 從中分配原始類型時,永遠不會對其進行修改。
然后,當
f()
完成執行時,s1
將被銷毀嗎?
可能不是。 除非您有非常原始的編譯器或故意禁用優化,否則可能在此處使用移動省略的優化(有時稱為“返回值優化”)。 s1
將在調用者的堆棧框架中創建,因此不需要任何工作即可將其返回。
函數執行后,我找不到找到s1值的方法
不,返回后無法檢查對象,因為它不再存在。 如果要查看移動的效果,可以移動到新的局部變量
S s2 = std::move(s1);
然后檢查s1
。 或者您可以編寫自己的move構造函數
S(S && other) : a(std::move(other.a)), b(b) {
std::cout << other.a << '\n';
}
但是,如上所述,這可能不會用於函數返回值。
首先,通過定義析構函數,您可以禁用隱式move構造函數。 您必須添加到您的代碼:
S(const S&) = default;
S(S&&) = default;
S& operator=(const S&) = default;
S& operator=(S&&) = default; // not required here but should be added for completeness
然后,無論如何,RVO發揮了作用。 如其他答案所述,允許編譯器取消對復制和移動構造函數的調用。 在GCC和clang中,可以通過添加-fno-elide-constructors
編譯器選項來禁用此功能。 之后,您將獲得以下輸出:
42 // moved s1 (this can theoretically be different, because the value of s1.a is unspecified)
0 // s0
tool 42 // arg
tool 42 // return value of f()
tool 42 // s3
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.