Несколько удивительно (для меня), следующие две программы компилируются с разными выходами, причем последняя имеет намного лучшую производительность (протестировано с 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.9 / 1:
Семантические описания в этом международном стандарте определяют
параметризованная недетерминированная абстрактная машина. Этот международный
Стандарты не предъявляют никаких требований к структуре соответствия
Реализации. В частности, им не нужно копировать или эмулировать
структура абстрактной машины. Скорее, соответствующие реализации
должны эмулировать (только) наблюдаемое поведение абстрактного
машина как объяснено ниже
то есть компилятор не может изменить наблюдаемое поведение программы. Автоматическое (даже если без последствий) преобразование назначения в назначение перемещения нарушит этот оператор.
Варианты копирования могут немного изменить это поведение, но это регулируется §12.8 / 31.
Если вы хотите использовать версию перемещения, вам придется явно запросить ее, как в последнем примере.
Давайте посмотрим на следующий пример (пожалуйста, игнорируйте 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
xvalue
в то время как это не xvalue
в момент вызова это изменит наблюдаемое поведение программы, и это не желательно и не разрешено стандартом.