12

有点令人惊讶(对我来说),以下两个程序编译为不同的输出,后一个程序具有更好的性能(用 gcc 和 clang 测试):

#include <vector>
int main()
{
    std::vector<int> a(2<<20);
    for(std::size_t i = 0; i != 1000; ++i) {
        std::vector<int> b(2<<20);
        a = b;
    }
}

对比

#include <vector>
int main()
{
    std::vector<int> a(2<<20);
    for(std::size_t i = 0; i != 1000; ++i) {
        std::vector<int> b(2<<20);
        a = std::move(b);
    }
}

有人可以向我解释为什么编译器不会(或不能)b在最后一个赋值中自动考虑一个 xvalue 并在没有显式转换的情况下应用移动语义std::move吗?

编辑:编译(g++|clang++) -std=c++11 -O3 -o test test.cpp

4

2 回答 2

7

编译器不能打破 as-if 规则

正如 §1.9/1 所述:

本国际标准中的语义描述定义了一个参数化的非确定性抽象机。本国际标准对一致性实现的结构没有要求。特别是,它们不需要复制或模仿抽象机器的结构。相反,需要符合要求的实现来模拟(仅)抽象机的可观察行为,如下所述

即编译器不能改变程序的可观察行为。自动(即使没有影响)将赋值转换为移动赋值会破坏这个语句。

复制省略可以稍微改变这种行为,但这是由 §12.8/31 规定的。

如果您想使用移动版本,则必须像后一个示例中那样明确要求它。

于 2014-09-24T09:42:59.887 回答
5

让我们看下一个示例(请忽略voidfrom 的返回类型operator=):

#include <iostream>

struct helper
{
    void operator=(helper&&){std::cout<<"move"<<std::endl;}
    void operator=(const helper&){std::cout<<"copy"<<std::endl;}
};

void fun()
{
    helper a;
    {
        helper b;
        a = b;
    }
}

void gun()
{
    helper a;
    {
        helper b;
        a = std::move(b);
    }
}
int main()
{
    fun();
    gun();
}

根据operator=其论点,它具有不同的行为。只有当编译器能够保持相同的可观察行为时,才允许编译器优化代码。

bfuna考虑xvalue,虽然在调用时它不是 a xvalue,但它会改变程序的可观察行为,这是标准所不希望的,也是不允许的。

于 2014-09-24T09:38:27.393 回答