用类的第一个条目填充std::向量的首选方法

Prefered method of populating a std::vector of classes with its first entry

本文关键字:向量 方法 std 填充 第一个      更新时间:2023-10-16

在审查一个大型软件项目时,我遇到了两种基本上相同的方法,即在std::vector 上推送初始条目

考虑像Foo 这样的类

class Foo
{
   public:
    Foo(int param){
      m_param = param;
    }
    setParam(int param){
      m_param = param;
    }
  private:
    int m_param;
}

考虑到任何适用的指标,以下两者之间是否有一种首选方法。。。。速度、稳定性等

Foo bar;
int val = 5;
bar.setParam(val);
std::vector<Foo> fooVec(1, bar);

int val = 5;
std::vector<Foo> fooVec;
fooVec.push_back(Foo(val));

考虑到任何适用的指标,以下两者之间是否有一种首选方法。。。。速度、稳定性等

可以说 毫无疑问这是一种糟糕的风格:

auto test1()
{
    Foo bar;               // redundant default construction
    int val = 5;           // redundant load
    bar.setParam(val);     // only now setting the value
    std::vector<Foo> fooVec(1, bar);  // redundant copy
    return fooVec;
}

这是一种很好的风格:

auto test2()
{
    return std::vector<Foo>(1, Foo(5));
}

表现怎么样,我们都很关心,对吧?

但这在现实中意味着什么?一旦您启用了优化?。。。

__Z5test1v:                             ## @_Z5test1v
    .cfi_startproc
## BB#0:                                ## %_ZNSt3__16vectorI3FooNS_9allocatorIS1_EEEC2EmRKS1_.exit1
    pushq   %rbx
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbx, -16
    movq    %rdi, %rbx
    movq    $0, 16(%rbx)
    movq    $0, 8(%rbx)
    movq    $0, (%rbx)
    movl    $4, %edi
    callq   __Znwm
    movq    %rax, (%rbx)
    leaq    4(%rax), %rcx
    movq    %rcx, 16(%rbx)
    movl    $5, (%rax)
    movq    %rcx, 8(%rbx)
    movq    %rbx, %rax
    popq    %rbx
    retq
    .cfi_endproc
    .globl  __Z5test2v
    .align  4, 0x90
__Z5test2v:                             ## @_Z5test2v
    .cfi_startproc
## BB#0:                                ## %_ZNSt3__16vectorI3FooNS_9allocatorIS1_EEEC2EmRKS1_.exit1
    pushq   %rbx
Ltmp2:
    .cfi_def_cfa_offset 16
Ltmp3:
    .cfi_offset %rbx, -16
    movq    %rdi, %rbx
    movq    $0, 16(%rbx)
    movq    $0, 8(%rbx)
    movq    $0, (%rbx)
    movl    $4, %edi
    callq   __Znwm
    movq    %rax, (%rbx)
    leaq    4(%rax), %rcx
    movq    %rcx, 16(%rbx)
    movl    $5, (%rax)
    movq    %rcx, 8(%rbx)
    movq    %rbx, %rax
    popq    %rbx
    retq
    .cfi_endproc

绝对没有任何区别。在这种情况下,生成的机器代码完全相同。

除非你有一个相当具体的理由使用其中一个,比如需要支持一个旧的(C++11之前的)编译器,否则我只会使用:

std::vector<Foo> fooVec { 5 }; // or fooVec { foo(5) };, if you really prefer

这几乎可以保证与其他任何一种一样快速、稳定等(可能会快一点,取决于…)