Разница в std::vector::emplace_back между GCC и VC++

Я слышал, что одна из рекомендаций 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 более близко к ожидаемому)?


person NetVipeC    schedule 08.12.2014    source источник
comment
Это open-std.org/jtc1/sc22/ wg21/docs/lwg-active.html#760 ?   -  person dyp    schedule 08.12.2014
comment
Интересно, почему и как можно ожидать, что это сработает.   -  person n. 1.8e9-where's-my-share m.    schedule 08.12.2014
comment
Связанный: безопасно ли отталкивать элемент из того же вектора?   -  person dyp    schedule 08.12.2014
comment
Вот мой документ в лучшем виде: http://htmlpreview.github.io/?https://github.com/HowardHinnant/papers/blob/master/insert_vs_emplace.html   -  person Howard Hinnant    schedule 08.12.2014
comment
comment
Да, ссылка open-std описывает именно эту проблему. К сожалению, предлагаемое решение добавляет к стандарту, что такие параметры (не должны быть элементами или подобъектами элементов контейнера, даже без диагностики).   -  person NetVipeC    schedule 08.12.2014
comment
Спасибо за ссылки и мудрость.   -  person NetVipeC    schedule 08.12.2014
comment
@dyp: Спасибо, готово: howardhinnant.github.io   -  person Howard Hinnant    schedule 08.12.2014
comment
Репост из комментарий @HowardHinnant по соответствующему вопросу: open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2164, где он возражает против предложенной резолюции LWG 760, на которую я ссылался выше.   -  person dyp    schedule 08.12.2014
comment
Что касается первого абзаца вашего вопроса, Скотт Мейерс совсем недавно выступил с докладом, в котором подробно рассказал об этом конкретном руководстве, которое стоит посмотреть: youtu.be/smqT9Io_bKo?t=14m (он также ссылается на бенчмарк Говарда Хиннанта, упомянутый в предыдущих комментариях)   -  person Max Galkin    schedule 21.12.2014


Ответы (1)


Когда вы использовали &operator[], это вернуло ссылку. Затем вы использовали emplace_back, что вызвало перераспределение и, таким образом, сделало недействительными все прошлые ссылки. Оба эти правила хорошо определены. Правильная вещь, которая должна произойти, это исключение. На самом деле я ожидаю, что версия VC++ вызовет исключение, если вы используете отладочную версию под отладчиком.

push_back имеет те же два правила, что означает, что он будет делать то же самое. Я почти уверен, что замена двух строк emplace_back/push_back приведет к такому же поведению.

person DraconPern    schedule 13.12.2014