[英]std::exchange working differently with VC++ and gcc
以下代碼:
#include <utility>
int main()
{
auto pos = 0;
auto rel = pos - std::exchange(pos, pos + 1);
return rel; // g++: 0, VC++: 1
}
如果您使用 VC++ 編譯器在rextester上嘗試代碼,則結果為 1,而godbolt 上的 gcc 結果為 0(使用 rextester 的 gcc 顯然不會返回結果)。
問題:為什么結果不同?
第二個問題:是否有任何工具可以檢查該錯誤? 任何叮當警告?
我的猜測是 VC++ 中的std::exchange
在評估另一個操作數之前被調用,而在 gcc 中並非如此。 如果您交換操作數pos
和std::exchange
,結果是 -1(或 255)VC++ 和 gcc。
這可能與副作用有關 - 並調用std::exchange
顯然有副作用。
幸運的是,在從 VC++ 轉換到 gcc 后,我通過單元測試發現了這個錯誤——起初有點慌張,把它歸結為這個簡單的(不是)工作示例。
二元運算符-
是與序列點相關聯的注釋,這意味着它沒有指定在A - B
計算表達式A
和B
順序:
考慮兩個函數
f()
和g()
。 在 C 和 C++ 中,+
運算符不與序列點相關聯,因此在表達式f()+g()
,可能先執行f()
或g()
。 [...] 在 C 和 C++ 中,評估這樣的表達式會產生未定義的行為。[
因此,您的程序具有未定義的行為,任何對交叉編譯器行為的分析都是徒勞的。
在標准語中,這是[intro.execution]/17 [強調我的]:
除非另有說明,否則對單個運算符的操作數和單個表達式的子表達式的求值是無序的。 [注意:在程序執行過程中多次求值的表達式中,其子表達式的無序和無序求值不需要在不同的求值中一致執行。 — end note ] 運算符的操作數的值計算在運算符的結果的值計算之前進行排序。 如果一個內存位置的副作用相對於同一內存位置的另一個副作用或使用同一內存位置中任何對象的值的值計算是未排序的,並且它們不是潛在的並發,則行為是未定義的。 [注意:下一節對潛在的並發計算施加了類似但更復雜的限制。 —尾注]
[示例:
void g(int i) { i = 7, i++, i++; // i becomes 9 i = i++ + 1; // the value of i is incremented i = i++ + i; // the behavior is undefined i = i + 1; // the value of i is incremented }
—結束示例]
“未定義行為”是一個非常重要的理解原則。 如果您還沒有了解它,那么您現在有機會解決這個問題。 如果行為沒有明確定義,那么它就是undefined ,這意味着像這樣的奇怪的事情可能並且將會發生。
對於給定的表達式a - f(a)
,絕對不需要編譯器按從左到右的順序執行。 定義的行為僅說明結果代表a
和f(a)
作為首先獨立評估的。
如果您希望您的程序產生一致、正確的結果,那么挑戰之一就是避免未定義的行為。 你怎么做到這一點? 編譯器不會告訴你什么時候執行它,它會默默地發生,它甚至可能在你使用代碼的所有條件下“工作”。
如果你想讓它真正正確,你需要知道大量的規則,比如行為是未定義的,你只需要尊重它。 真的沒有辦法解決。 就像處理迭代器失效、使用后釋放和使用未初始化的變量一樣,您有相當大的責任來正確編碼。 這就是為什么 C++ 很難正確編碼的原因。
總之,其他語言對這樣的表達式中的執行順序做出了具體保證。 C++ 沒有。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.