Автоматическая оптимизация xvalue

Несколько неожиданно (для меня) следующие две программы скомпилированы для разных выходов, причем последняя обладает гораздо лучшей производительностью (проверена с помощью 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

Ответ 1

Компиляторы не могут нарушить правило as-if

Как сказано в §1.9/1:

Семантические описания в этом Международном стандарте определяют параметризованной недетерминированной абстрактной машиной. Этот международный Стандарт не требует от структуры соответствия Реализации. В частности, им не нужно копировать или эмулировать структура абстрактной машины. Скорее, соответствующие реализации требуются для подражания (только) наблюдаемого поведения абстрактного машина, как описано ниже

то есть. компилятор не может изменить наблюдаемое поведение программы. Автоматически (даже если без каких-либо последствий) преобразование присвоения в присваивание переадресации нарушит это утверждение.

Копирование эльфов может слегка изменить это поведение, но это регулируется §12.8/31.

Если вы хотите использовать версию перемещения, вам придется явно запросить ее, как в последнем примере.

Ответ 2

Посмотрим на следующий образец (пожалуйста, игнорируйте void тип возврата из 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= имеет различное поведение в зависимости от его аргументов. Компилятору разрешено оптимизировать код только в том случае, если он способен поддерживать наблюдаемое поведение одинаково.

Учитывая b из fun an xvalue, в то время как он не является xvalue в момент вызова, он изменит наблюдаемое поведение программы, и это нежелательно и не допускается стандартом.