簡體   English   中英

emplace_back與push_back的原始類型

[英]emplace_back vs. push_back for primitive types

我想知道當使用原始標量類型時std::vectoremplace_backpush_back方法是否有任何不同,例如std::uint32_tstd::uint8_t 直覺上,我猜想,在編譯之后,兩個變體都會在這里導致相同的字節碼:

void copyListContent(std::uint8_t * list, std::size_t nElems,
                     std::vector<std::uint8_t> & vec)
{
    vec.clear();
    vec.reserve(nElems);
    for (std::size_t i = 0; i < nElems; ++i)
    {
        //variant 1:
        vec.push_back(list[i]);
        //variant 2:
        vec.emplace_back(list[i]);
    }        
}

如果那應該是錯的,請糾正我...

現在,我開始掙扎的地方是,當我問自己如果“list”和vector的類型不匹配會發生什么:

void copyListContent(std::uint8_t * list, std::size_t nElems,
                     std::vector<std::uint32_t> & vec)
{
    //... same code as above      
}

std::uint8_t元素在將它們放入向量時會被轉換為std::uint32_t (使用emplace_backpush_back ),所以我想知道是否會觸發一些“構造函數”被調用? 在這種情況下, emplace_back會更有效率,因為它會避免構造一個可以復制的臨時對象嗎? 或者這些隱式轉換是否有任何區別,而emplace_backpush_back行為相同?

所以,我問自己和你: 對於像這樣的原始類型, emplace_backpush_back總是表現得相似嗎?

作為一個模糊的猜測,我會說“可能是的”,但我對C ++內部的知識不夠,無法為我自己解決這個問題。 我很樂意在這種情況下了解事情是如何運作的 - 非常感謝!

GCC將兩個版本的代碼編譯為相同的結果程序集( Godbolt.org ):

#include<vector>

void push(std::vector<int> & vec, int val) {
    vec.push_back(val);
}

VS

#include<vector>

void push(std::vector<int> & vec, int val) {
    vec.emplace_back(val);
}

兩者都導致以下組裝:

push(std::vector<int, std::allocator<int> >&, int):
        push    r15
        push    r14
        push    r13
        push    r12
        push    rbp
        push    rbx
        sub     rsp, 24
        mov     rbx, QWORD PTR [rdi+8]
        cmp     rbx, QWORD PTR [rdi+16]
        je      .L2
        mov     DWORD PTR [rbx], esi
        add     rbx, 4
        mov     QWORD PTR [rdi+8], rbx
        add     rsp, 24
        pop     rbx
        pop     rbp
        pop     r12
        pop     r13
        pop     r14
        pop     r15
        ret
.L2:
        mov     r12, QWORD PTR [rdi]
        mov     r14, rbx
        mov     ecx, esi
        mov     rbp, rdi
        sub     r14, r12
        mov     rax, r14
        sar     rax, 2
        je      .L9
        lea     rdx, [rax+rax]
        mov     r15, -4
        cmp     rax, rdx
        ja      .L4
        movabs  rsi, 4611686018427387903
        cmp     rdx, rsi
        jbe     .L19
.L4:
        mov     rdi, r15
        mov     DWORD PTR [rsp], ecx
        call    operator new(unsigned long)
        mov     ecx, DWORD PTR [rsp]
        mov     r13, rax
        add     r15, rax
.L5:
        lea     rax, [r13+4+r14]
        mov     DWORD PTR [r13+0+r14], ecx
        mov     QWORD PTR [rsp], rax
        cmp     rbx, r12
        je      .L6
        mov     rdx, r14
        mov     rsi, r12
        mov     rdi, r13
        call    memmove
.L7:
        mov     rdi, r12
        call    operator delete(void*)
.L8:
        mov     QWORD PTR [rsp+8], r13
        movq    xmm0, QWORD PTR [rsp+8]
        mov     QWORD PTR [rbp+16], r15
        movhps  xmm0, QWORD PTR [rsp]
        movups  XMMWORD PTR [rbp+0], xmm0
        add     rsp, 24
        pop     rbx
        pop     rbp
        pop     r12
        pop     r13
        pop     r14
        pop     r15
        ret
.L6:
        test    r12, r12
        je      .L8
        jmp     .L7
.L9:
        mov     r15d, 4
        jmp     .L4
.L19:
        xor     r15d, r15d
        xor     r13d, r13d
        test    rdx, rdx
        je      .L5
        lea     r15, [0+rax*8]
        jmp     .L4

正如您可能已經推斷的那樣,在處理具有更復雜構造/復制/移動行為的類型時,這不是您可以依賴的行為,但對於原始類型,差異可以忽略不計。

話雖如此,有一種情況可能會有所不同:

std::vector<int16_t> vec;
size_t seed = 0x123456789abcdef;

vec.push_back(seed);

VS

vec.emplace_back(seed);

在(適當優化的)編譯器中,兩個匯編代碼可能是相同的,但是您將從編譯器獲得不同的縮小警告(或錯誤,如果強制警告導致編譯失敗)。 后者更有可能提供難以診斷的警告消息,因為錯誤將來自內部<vector>而不是內部任何.cpp文件進行調用。

在Abseil網站上發布的Google指南: https ://abseil.io/tips/112,您應該更喜歡使用push_back,因為它更具可讀性。

擔心內置類型的隱式轉換似乎過早優化; 很有可能你的編譯器會優化轉換。

暫無
暫無

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

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