C ++ 11 rvalue и move: может ли унаследованный код избежать копирования?

Пожалуйста, поймите, что я все еще изучаю концепции семантики перемещения и значения в C ++ 11. Мой вопрос заключается в том, может ли унаследованный код получить бесплатный обед, избегая ненужного копирования, просто используя компиляторы C ++ 11 и STL.

Вот очень простой пример. Этот код создает простую таблицу частот символов для данной строки. Например, «яблоко» должно вернуться {('a', 1), ('e', 1), ('l', 1), ('p', 2)}, Как вы увидите, я просто использую векторы в качестве значений.

typedef std::tuple<char, int> Frequency;
typedef std::vector<Frequency> Frequencies;

Frequencies buildFrequenciesTable(std::string w) {
char table['z' - 'a' + 1] = { 0, };
std::for_each(w.cbegin(), w.cend(), [&table](char c) {
++table[::tolower(c) - 'a'];
});

Frequencies freqs;
for (size_t i = 0; i < 'z' - 'a' + 1; ++i) {
if (table[i] != 0)
freqs.push_back(tuple<char, int>((char) ('a' + i), table[i]));
}
return freqs; // Q1: Is vector get copied?
}

int main() {
using namespace std;

Frequencies f1 = buildFrequenciesTable("apple"); // Q2: Copy?
Frequencies f2 = buildFrequenciesTable("banana");
vector<Frequencies> fs = { f1, f2 }; // Q3: Copy?
}

Ясно, что C ++ 03 генерирует весь код копирования (используя конструкторы копирования и операторы присваивания) при возврате векторов в качестве значения. Как насчет C ++ 11? std::vector конструктор ходов. Может ли этот код избежать ненужных копий? Или я должен использовать либо && или же std::forward в приведенном коде?

Я пытался отладить внутренний код STL, но это было трудно убедить.

Примечание. Моя цель — свести к минимуму ненужные копии этих функций. Я знаю, что мог бы использовать новые / указатели / ссылки, но это должно решить проблему утечки памяти. Итак, я хотел бы использовать значения как можно больше.

2

Решение

Для Q1, скорее всего, нет копии даже в C ++ 03, поскольку копия удаляется с помощью «Оптимизации именованных возвращаемых значений» (NRVO).

Для Q2 также, скорее всего, нет копии даже в C ++ 03, поскольку copy-elision удаляет ее.

Для Q3, даже в C ++ 11 вы делать есть копии, которые нужно пометить f1 а также f2 как подвижные для того, чтобы фактически переместить их:

vector<Frequencies> fs = { std::move(f1), std::move(f2) };

Так как вы задали несколько вопросов, я думаю, что я опущу дальнейшие объяснения, посмотрите NRVO, copy-elision и где std::move требуется, спросите, если у вас есть какие-либо дополнительные вопросы.

Однако в некоторых случаях вы можете получить бесплатные ходы, например, если есть временный объект, который можно переместить:

vector<Frequencies> fs = { buildFrequenciesTable("apple"),
buildFrequenciesTable("bananas") };

Вышеуказанное обнаружит два вектора, возвращенные из buildFrequenciesTable() как временные и, следовательно, они будут перемещены в fs,

5

Другие решения

Возврат вектора из функции (Q1) будет использовать семантику перемещения, где это возможно, без необходимости изменения кода. Аналогично, инициализация векторов из возвращенного временного (Q2) будет использовать семантику перемещения; возвращаемое значение Rvalue, так можно перенести с. На практике оба хода (или, исторически, копии) должны быть исключены, чтобы инициализировать функцию f1 а также f2 непосредственно без перемещения или копирования.

Положить их в вектор (Q3) требует копирования: переменные lvalues, который не может быть неявно перемещен. Так что вам придется использовать std::moveили реструктурируйте код, чтобы избежать этих копий.

1

По вопросам рекламы [email protected]