簡體   English   中英

默認移動構造

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

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