gpt4 book ai didi

c++ - 原始类型的 emplace_back 与 push_back

转载 作者:太空狗 更新时间:2023-10-29 20:34:30 27 4
gpt4 key购买 nike

我想知道 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]);
}
}

如果那应该已经错了,请纠正我...

现在,当我问自己如果“列表”和 vector 的类型不匹配会发生什么时,我开始纠结:

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

std::uint8_t 元素在将它们放入 vector 时将被转换为 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);
}

对比

#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);

对比

vec.emplace_back(seed);

在(适当优化的)编译器中,这两个汇编代码可能是相同的,但是您会从编译器得到不同的缩小警告(或错误,如果您强制警告导致编译失败)。后者更有可能给出难以诊断的警告消息,因为错误将源自 <vector> 内部,而不是调用的任何 .cpp 文件内部。

关于c++ - 原始类型的 emplace_back 与 push_back,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48158286/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com