Я слышал, что одна из рекомендаций Modern C++ — использовать emplace_back
вместо push_back
для добавления в контейнеры (emplace_back
принимать любую версию параметров любого конструктора типа storage в контейнере).
Согласно стандартному проекту N3797 23.3.6.5 (1), скажите, что:
Примечания. Вызывает перераспределение, если новый размер превышает старую емкость. Если перераспределения не происходит, все итераторы и ссылки до точки вставки остаются действительными. Если исключение выдается не конструктором копирования, конструктором перемещения, оператором присваивания или оператором присваивания перемещения T или любой операцией InputIterator, никаких последствий не возникает. Если конструктор перемещения не-CopyInsertable T выдает исключение, последствия не указаны.
Это указывает, что происходит, когда перераспределение не требуется, но оставляет проблему открытой, когда контейнеру нужно расти.
В этом фрагменте кода:
#include <iostream>
#include <vector>
int main() {
std::vector<unsigned char> buff {1, 2, 3, 4};
buff.emplace_back(buff[0]);
buff.push_back(buff[1]);
for (const auto& c : buff) {
std::cout << std::hex << static_cast<long>(c) << ", ";
}
std::cout << std::endl;
return 0;
}
Скомпилировано с помощью VC++ (Visual Studio 2013, обновление 4) и GCC 4.9.1 (MinGW) в Debug в Windows 8.1.
При компиляции с помощью VC++ вывод:
1, 2, 3, 4, dd, 2
При компиляции с GCC вывод:
1, 2, 3, 4, 1, 2
Проверяя реализацию emplace_back
в VC++, разница в том, что первые строки кода проверяют, нужно ли контейнеру расти (и расти, если нужно), в случае, если контейнеру нужно расти, ссылка на первый элемент (< strong>buff[0]), полученный в методе emplace_back
, становится недействительным, и когда происходит фактическая установка значения в новом созданном элементе контейнера, значение становится недействительным.
В случае с push_back
работает, потому что создание элемента для добавления производится в привязке параметров (до возможного роста контейнера).
Мой вопрос: такое поведение, когда контейнеру нужно расти, потому что вызов emplace_back
и параметр является ссылкой на тот же контейнер, определена реализацией, не указана или есть проблема в реализации одного из компилятора ( предположим, в VC++, так как поведение GCC более близко к ожидаемому)?
open-std
описывает именно эту проблему. К сожалению, предлагаемое решение добавляет к стандарту, что такие параметры (не должны быть элементами или подобъектами элементов контейнера, даже без диагностики). - person NetVipeC   schedule 08.12.2014