[英]emplace_back vs. push_back for primitive types
我想知道當使用原始標量類型時std::vector
的emplace_back
和push_back
方法是否有任何不同,例如std::uint32_t
或std::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_back
或push_back
),所以我想知道是否會觸發一些“構造函數”被調用? 在這種情況下, emplace_back
會更有效率,因為它會避免構造一個可以復制的臨時對象嗎? 或者這些隱式轉換是否有任何區別,而emplace_back
和push_back
行為相同?
所以,我問自己和你: 對於像這樣的原始類型, emplace_back
和push_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.