簡體   English   中英

c++ 參數為 const T vs const T&

[英]c++ parameter as const T vs const T&

假設SomeDataStruct是“巨大的”,現代編譯器會在以下兩種情況下產生等效且同樣有效的代碼嗎?

1) void fooByValue(const SomeDataStruct data);   

2) void fooByReference(const SomeDataStruct& data);

如果等效,哪個成語是“首選”,為什么?

請注意,這個問題與這個問題相似:

https://softwareengineering.stackexchange.com/questions/372105/is-passing-arguments-as-const-references-premature-optimization

但不完全相同,因為我在這兩個函數中都使用了“ const ”; 在提到的鏈接中, fooByValue只是

1) void fooByValue(SomeDataStruct data);

編輯:假設SomeDataStruct類型沒有任何復制構造函數。

頂級const (不在指針/引用后面)在聲明中無效。

// Even if the declaration uses const:
void fooByValue(const SomeDataStruct data);

// The definition without `const` is still considered a valid definition for that declaration.
// It's not a different overload!
// (this oddity is inherited from C, where overloading is a compiler error but this was always allowed)
void fooByValue(SomeDataStruct data)
{
   data.member = 0;
}

所以編譯器被迫忽略const並且必須假設參數可能被修改--> 副本是必要的。 但是,如果在 function 調用之后未使用原始復制自變量,編譯器可能仍會優化副本。

C++ 標准的相關部分是 9.3.3.5 功能:

使用以下規則確定 function 的類型。 [..] 生成參數類型列表后,在形成 function 類型時,會刪除任何修改參數類型的頂級 cv 限定符。

假設 SomeDataStruct 是“巨大的”,現代編譯器會在以下兩種情況下產生等效且同樣有效的代碼嗎?

這取決於編譯器,您如何定義 object,function 中的 object 發生了什么。 讓我們看看 gcc 在 x86-64 Linux 上做了什么。 看看下面的代碼:

struct big_
{
    unsigned long w,x,y,z;
    int pos;
};

unsigned long byval(const big_ big)
{
    auto y = big.z;
    y += big.y;
    y += big.x;
    y += big.w;
    return y;
}

unsigned long byref(const big_& big)
{
    auto y = big.z;
    y += big.y;
    y += big.x;
    y += big.w;
    return y;
}

g++ -std=c++17 -O3編譯它會給我以下裝配Godbolt 鏈接

byval(big_):
        mov     rax, QWORD PTR [rsp+24]
        add     rax, QWORD PTR [rsp+32]
        add     rax, QWORD PTR [rsp+16]
        add     rax, QWORD PTR [rsp+8]
        ret
byref(big_ const&):
        mov     rax, QWORD PTR [rdi+16]
        add     rax, QWORD PTR [rdi+24]
        add     rax, QWORD PTR [rdi+8]
        add     rax, QWORD PTR [rdi]
        ret

在上面的代碼中我們可以看到什么?

arguments 到第一個 function, byval在堆棧上傳遞。 arguments 到第二個 function 通過寄存器rdi傳遞(請參閱操作系統的調用約定以找出原因)。 通過寄存器傳遞任何東西總是比在堆棧上傳遞更快,因為寄存器更靠近cpu ,而堆棧位於緩存或內存中的某個位置。 因此在這里通過引用傳遞更好。 如果你有一個小的 object(8 字節),通過值傳遞它會更好,因為它無論如何都會通過寄存器傳遞。 僅當您的 object 太大以至於無法放入寄存器時,才會發生堆棧傳遞。

我們可以看到的另一件事是byval有一個不是const的參數。 編譯器剛剛刪除了它。

因為我在這兩個函數中都使用了“const”;

從上面的解釋中可以看出,這無關緊要。

void fooByValue(SomeDataStruct data); 編輯:假設 SomeDataStruct 類型沒有任何復制構造函數。

如果您沒有編寫復制構造函數,這並不意味着編譯器不能為您隱式生成一個。 但是讓我們假設你刪除了它。 所以現在你不能復制它,如果你嘗試,你的代碼將無法編譯。 但是,如果您定義了移動構造函數,則可以移動它。

void fn()
{
    fooByValue( std::move(data) ); // no copy
}

使用const不會改變代碼的性能。 const就像您和編譯器之間的合同。 當您將某些內容標記為const時,編譯器將不允許您更改它。 如果您以某種方式更改它,它將導致未定義的行為。 我建議您閱讀 go 並閱讀 Arthur O'Dwyer 關於此主題的這篇文章const is a contract。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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