[英]C++ static initialization
在下列情況下應該是什么行為:
class C {
boost::mutex mutex_;
std::map<...> data_;
};
C& get() {
static C c;
return c;
}
int main() {
get(); // is compiler free to optimize out the call?
....
}
編譯器是否允許優化調用get()
?
我的想法是在多線程操作需要之前觸摸靜態變量來初始化它
這是一個更好的選擇嗎?:
C& get() {
static C *c = new C();
return *c;
}
C和C ++標准在一個相當簡單的原則下運行,通常被稱為“as-if規則” - 基本上,只要沒有符合規范的代碼能夠辨別它做了什么和做什么之間的差異,編譯器就可以自由地做任何事情。是正式要求的。
我沒有看到符合代碼的方法來辨別在這種情況下是否實際調用了get
,所以在我看來它可以自由地優化它。
根據您的編輯,這是一個改進的版本,具有相同的結果。
輸入:
struct C {
int myfrob;
int frob();
C(int f);
};
C::C(int f) : myfrob(f) {}
int C::frob() { return myfrob; }
C& get() {
static C *c = new C(5);
return *c;
}
int main() {
return get().frob(); // is compiler free to optimize out the call?
}
輸出:
; ModuleID = '/tmp/webcompile/_28088_0.bc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-linux-gnu"
%struct.C = type { i32 }
@guard variable for get()::c = internal global i64 0 ; <i64*> [#uses=4]
declare i32 @__cxa_guard_acquire(i64*) nounwind
declare i8* @operator new(unsigned long)(i64)
declare void @__cxa_guard_release(i64*) nounwind
declare i8* @llvm.eh.exception() nounwind readonly
declare i32 @llvm.eh.selector(i8*, i8*, ...) nounwind
declare void @__cxa_guard_abort(i64*) nounwind
declare i32 @__gxx_personality_v0(...)
declare void @_Unwind_Resume_or_Rethrow(i8*)
define i32 @main() {
entry:
%0 = load i8* bitcast (i64* @guard variable for get()::c to i8*), align 8 ; <i8> [#uses=1]
%1 = icmp eq i8 %0, 0 ; <i1> [#uses=1]
br i1 %1, label %bb.i, label %_Z3getv.exit
bb.i: ; preds = %entry
%2 = tail call i32 @__cxa_guard_acquire(i64* @guard variable for get()::c) nounwind ; <i32> [#uses=1]
%3 = icmp eq i32 %2, 0 ; <i1> [#uses=1]
br i1 %3, label %_Z3getv.exit, label %bb1.i
bb1.i: ; preds = %bb.i
%4 = invoke i8* @operator new(unsigned long)(i64 4)
to label %invcont.i unwind label %lpad.i ; <i8*> [#uses=2]
invcont.i: ; preds = %bb1.i
%5 = bitcast i8* %4 to %struct.C* ; <%struct.C*> [#uses=1]
%6 = bitcast i8* %4 to i32* ; <i32*> [#uses=1]
store i32 5, i32* %6, align 4
tail call void @__cxa_guard_release(i64* @guard variable for get()::c) nounwind
br label %_Z3getv.exit
lpad.i: ; preds = %bb1.i
%eh_ptr.i = tail call i8* @llvm.eh.exception() ; <i8*> [#uses=2]
%eh_select12.i = tail call i32 (i8*, i8*, ...)* @llvm.eh.selector(i8* %eh_ptr.i, i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*), i8* null) ; <i32> [#uses=0]
tail call void @__cxa_guard_abort(i64* @guard variable for get()::c) nounwind
tail call void @_Unwind_Resume_or_Rethrow(i8* %eh_ptr.i)
unreachable
_Z3getv.exit: ; preds = %invcont.i, %bb.i, %entry
%_ZZ3getvE1c.0 = phi %struct.C* [ null, %bb.i ], [ %5, %invcont.i ], [ null, %entry ] ; <%struct.C*> [#uses=1]
%7 = getelementptr inbounds %struct.C* %_ZZ3getvE1c.0, i64 0, i32 0 ; <i32*> [#uses=1]
%8 = load i32* %7, align 4 ; <i32> [#uses=1]
ret i32 %8
}
注意,沒有為:: get發出代碼,但是main仍然根據需要使用保護變量分配:: get :: c(at%4)(在%2和invcont.i和lpad.i的末尾)。 llvm這里列出了所有這些東西。
tl; dr:不要擔心,優化器通常會正確地獲取這些東西。 你看到錯誤了嗎?
編譯器是否優化函數調用基本上是未指定的行為,根據標准。 未指定的行為基本上是從一組有限可能性中選擇的行為,但是每次選擇可能不一致。 在這種情況下,選擇是'優化'或'不',標准沒有指定,並且實現也不應該記錄,因為它是給定實現可能不一致采用的選擇。
如果想法只是“觸摸”,如果我們只是在每個調用中添加一個虛擬的volatile變量並且虛擬遞增它,它會有所幫助
例如
C& getC(){
volatile int dummy;
dummy++;
// rest of the code
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.