编译器可以忽略以下副本吗?

| 我仍然是新手程序员,我知道过早的优化是不好的,但是我也知道复制大量内容也是不好的。 我已经阅读了复制要素及其同义词,但例如在Wikipedia上的示例使我似乎只有在要返回的对象在完全构造的同时返回时,复制要素才能发生。 向量之类的对象呢?通常仅在填充某些东西并用作返回值时才有意义。 毕竟,空向量只能手动实例化。 那么,它在这种情况下是否也起作用? 简洁的坏风格:
vector<foo> bar(string baz)
{
    vector<foo> out;
    for (each letter in baz)
        out.push_back(someTable[letter]);

    return out;
}

int main()
{
     vector<foo> oof = bar(\"Hello World\");
}
使用bar(vector&out,字符串文本)我没有真正的麻烦,但是上面的方法看起来更好,从美学上来说也有意图。     
已邀请:
  以维基百科上的示例为例,在我看来,仅当要返回的对象在完全构造好同时返回时才可以进行复制省略。 那是误导(读:错误)。问题在于,所有代码路径中仅返回一个对象,即潜在返回对象的仅一种构造正在发生。 您的代码很好,任何现代编译器都可以删除该副本。 另一方面,以下代码可能会产生问题:
vector<int> foo() {
    vector<int> a;
    vector<int> b;
    // … fill both.
    bool c;
    std::cin >> c;
    if (c) return a; else return b;
}
在这里,编译器需要完全构造两个不同的对象,并且只有稍后再决定要返回哪个对象,因此编译器必须复制一次,因为它无法直接在目标内存位置构造返回的对象。     
没有什么可以阻止编译器删除副本。在12.8.15中定义:   [...]复制操作的省略是   在以下允许   情况(可能会合并   消除多个副本):      [...]         当一个临时类对象具有   未绑定到参考(12.2)   将被复制到一个类对象   相同的简历不合格类型,副本   操作可以通过以下方式省略   构造临时对象   直接进入目标   省略的副本    是否确实取决于编译器和您使用的设置。     
vector
的两个隐含副本都可以并且经常被删除。命名返回值优化可以消除返回语句
return out;
中隐含的副本,并且也可以消除eliminated4ѭ副本初始化中所隐含的。 在同时进行两种优化的情况下,
vector<foo> out;
中构造的对象与
oof
中的对象相同。 使用诸如此类的人工测试用例,可以更轻松地测试正在执行哪些优化。
struct CopyMe
{
    CopyMe();
    CopyMe(const CopyMe& x);
    CopyMe& operator=(const CopyMe& x);

    char data[1024]; // give it some bulk
};

void Mutate(CopyMe&);

CopyMe fn()
{
    CopyMe x;
    Mutate(x);
    return x;
}

int main()
{
    CopyMe y = fn();
    return 0;
}
复制构造函数已声明但未定义,因此无法内联和消除对其的调用。使用现在相对较旧的gcc 4.4进行编译,结果为
-O3 -fno-inline
(过滤为C ++名称分解并进行编辑以删除非代码)。
fn():
        pushq   %rbx
        movq    %rdi, %rbx
        call    CopyMe::CopyMe()
        movq    %rbx, %rdi
        call    Mutate(CopyMe&)
        movq    %rbx, %rax
        popq    %rbx
        ret

main:
        subq    $1032, %rsp
        movq    %rsp, %rdi
        call    fn()
        xorl    %eax, %eax
        addq    $1032, %rsp
        ret
可以看出,没有对复制构造函数的调用。实际上,即使在
-O0
,gcc也会执行这些优化。您必须提供ѭ11才能关闭此行为;如果执行此操作,则gcc将对
CopyMe
的副本构造函数生成两次调用-在对
fn()
的调用内部进行一次,在外部进行一次调用。
fn():
        movq    %rbx, -16(%rsp)
        movq    %rbp, -8(%rsp)
        subq    $1048, %rsp
        movq    %rdi, %rbx
        movq    %rsp, %rdi
        call    CopyMe::CopyMe()
        movq    %rsp, %rdi
        call    Mutate(CopyMe&)
        movq    %rsp, %rsi
        movq    %rbx, %rdi
        call    CopyMe::CopyMe(CopyMe const&)
        movq    %rbx, %rax
        movq    1040(%rsp), %rbp
        movq    1032(%rsp), %rbx
        addq    $1048, %rsp
        ret

main:
        pushq   %rbx
        subq    $2048, %rsp
        movq    %rsp, %rdi
        call    fn()
        leaq    1024(%rsp), %rdi
        movq    %rsp, %rsi
        call    CopyMe::CopyMe(CopyMe const&)
        xorl    %eax, %eax
        addq    $2048, %rsp
        popq    %rbx
        ret
    

要回复问题请先登录注册