Мне известны 5 общих категорий, в которых перекомпиляция компилятора C ++ 03 в C ++ 11 может привести к неограниченному увеличению производительности, которое практически не связано с качеством реализации. Это все вариации семантики перемещения.
std::vector
перераспределять
struct bar{
std::vector<int> data;
};
std::vector<bar> foo(1);
foo.back().data.push_back(3);
foo.reserve(10); // two allocations and a delete occur in C++03
каждый раз , когда foo
буфер «s перераспределяется в C ++ 03 он копируется каждый vector
в bar
.
В C ++ 11 вместо этого перемещается bar::data
s, что в основном бесплатно.
В этом случае это зависит от оптимизации внутри std
контейнера vector
. В каждом случае ниже использование std
контейнеров только потому, что они являются объектами C ++, которые имеют эффективную move
семантику в C ++ 11 «автоматически» при обновлении компилятора. Объекты, которые не блокируют его и содержат std
контейнер, также наследуют автоматически улучшенные move
конструкторы.
НРВО провал
Когда NRVO (оптимизация именованного возвращаемого значения) завершается неудачно, в C ++ 03 он возвращается к копии, а в C ++ 11 - к перемещению. Неудачи НРВО легки:
std::vector<int> foo(int count){
std::vector<int> v; // oops
if (count<=0) return std::vector<int>();
v.reserve(count);
for(int i=0;i<count;++i)
v.push_back(i);
return v;
}
или даже:
std::vector<int> foo(bool which) {
std::vector<int> a, b;
// do work, filling a and b, using the other for calculations
if (which)
return a;
else
return b;
}
У нас есть три значения - возвращаемое значение и два разных значения внутри функции. Elision позволяет объединять значения в функции с возвращаемым значением, но не друг с другом. Они оба не могут быть объединены с возвращаемым значением без объединения друг с другом.
Основная проблема заключается в том, что элиминация NRVO является хрупкой, и код с изменениями, не находящимися рядом с return
сайтом, может внезапно привести к значительному снижению производительности в этом месте без использования диагностической информации. В большинстве случаев сбоя NRVO C ++ 11 заканчивается move
копией, а C ++ 03 заканчивается копией.
Возврат аргумента функции
Исключение также невозможно здесь:
std::set<int> func(std::set<int> in){
return in;
}
в C ++ 11 это дешево: в C ++ 03 нет способа избежать копирования. Аргументы функций не могут быть исключены с возвращаемым значением, потому что время жизни и местоположение параметра и возвращаемого значения управляются вызывающим кодом.
Тем не менее, C ++ 11 может переходить от одного к другому. (В менее игрушечном примере что-то может быть сделано с set
).
push_back
или insert
Наконец, исключение в контейнеры не происходит: но C ++ 11 перегружает rvalue, перемещает операторы вставки, что сохраняет копии.
struct whatever {
std::string data;
int count;
whatever( std::string d, int c ):data(d), count(c) {}
};
std::vector<whatever> v;
v.push_back( whatever("some long string goes here", 3) );
в C ++ 03 создается временный объект whatever
, затем он копируется в вектор v
. std::string
Выделено 2 буфера, каждый с одинаковыми данными, а один отбрасывается.
В C ++ 11 временный whatever
создается. whatever&&
push_back
Перегрузки , то move
с , что временная в вектор v
. Один std::string
буфер выделяется и перемещается в вектор. Пустое std::string
отбрасывается.
присваивание
Украденный из ответа @ Jarod42 ниже.
Исключение не может произойти с назначением, но может произойти.
std::set<int> some_function();
std::set<int> some_value;
// code
some_value = some_function();
здесь some_function
возвращает кандидата для исключения, но поскольку он не используется для непосредственного создания объекта, он не может быть исключен. В C ++ 03 вышеприведенное приводит к тому, что содержимое временного объекта копируется в some_value
. В C ++ 11 он перемещен some_value
, что в основном бесплатно.
Для полного эффекта вышеперечисленного вам нужен компилятор, который синтезирует конструкторы перемещения и присваивания для вас.
MSVC 2013 реализует конструкторы перемещения в std
контейнерах, но не синтезирует конструкторы перемещения для ваших типов.
Таким образом, типы, содержащие std::vector
s и подобные, не получат таких улучшений в MSVC2013, но начнут получать их в MSVC2015.
clang и gcc уже давно реализовали неявные конструкторы перемещения. Компилятор Intel 2013 года будет поддерживать неявную генерацию конструкторов перемещения, если вы пропустите -Qoption,cpp,--gen_move_operations
(по умолчанию они этого не делают, пытаясь обеспечить кросс-совместимость с MSVC2013).