Emplace c что это
стек emplace () в C ++ STL
Стеки являются типом контейнерных адаптеров с рабочим типом LIFO (Last In First Out), где новый элемент добавляется с одного конца, а (сверху) элемент удаляется только с этого конца.
стек :: устанавливать ()
Эта функция используется для вставки нового элемента в контейнер стека, новый элемент добавляется поверх стека.
Синтаксис:
Ошибки и исключения
1. Он имеет сильную гарантию исключения, поэтому никакие изменения не вносятся, если выдается исключение.
2. Параметр должен быть того же типа, что и контейнер, в противном случае выдается ошибка.
// Программа CPP для иллюстрации
// Реализация функции emplace ()
#include
#include
using namespace std;
stack int > mystack;
// стек становится 1, 2, 3, 4, 5, 6
Сложность времени: O (1)
Заявка :
Учитывая количество целых чисел, добавьте их в стек с помощью emplace () и найдите размер стека без использования функции размера.
Алгоритм
1. Вставьте данные элементы в контейнер стека, используя emplace () один за другим.
2. Продолжайте выталкивать элементы стека до тех пор, пока он не станет пустым, и увеличивайте значение переменной счетчика.
3. Распечатайте переменную счетчика.
// Программа CPP для иллюстрации
// Применение функции emplace ()
#include
#include
функция вектора emplace () в C ++ STL
Vector :: emplace () — это STL в C ++, который расширяет контейнер, вставляя новый элемент в позицию. Перераспределение происходит только в том случае, если требуется больше места. Здесь размер контейнера увеличивается на единицу.
Синтаксис:
Параметр:
Функция принимает два обязательных параметра, которые указаны ниже:
Возвращаемое значение: функция возвращает итератор, который указывает на вновь вставленный элемент.
Ниже программы иллюстрирует вышеуказанную функцию:
Программа 1:
// C ++ программа для иллюстрации
// функция vector :: emplace ()
// вставка спереди
#include
using namespace std;
// вставляем элемент с помощью функции emplace
auto it = vec.emplace(vec.begin(), 15);
// выводим элементы вектора
cout «The vector elements are: » ;
Программа 2:
// C ++ программа для иллюстрации
// функция vector :: emplace ()
// вставка в конце
#include
using namespace std;
// вставляем элемент с помощью функции emplace
auto it = vec.emplace(vec.end(), 16);
// выводим элементы вектора
cout «The vector elements are: » ;
Программа 3:
// C ++ программа для иллюстрации
// функция vector :: emplace ()
// вставка в середине
#include
using namespace std;
// вставляем элемент с помощью функции emplace
Разница между push_back и emplace_back
3 ответа 3
Как видите, push_back ‘у нужен объект типа элемента (или приводимый). А вот emplace ‘у просто передаются аргументы, как конструктору.
P.S. Понятно, что этим конструктором может быть и копирующий конструктор 🙂 Так что
push_back добавляет копию объекта (или обеспечивает перемещение, если возможно), а emplace_back создает объект непосредственно в конце вектора, т.е. без лишнего копирования (или перемещения).
Я бы сказал, что push_back более универсален. С emplace_back есть нюансы в более сложных алгоритмах. Например при копировании элементов многомерного вектора в одномерный. С виду работает, а компилируешь, ругается. Я бы сказал они один другой дополняют. Если emplace_back работает оставляем его, а если дивные ошибки лезут, то ставим push_back и добавляем <> как я выше показал.
Всё ещё ищете ответ? Посмотрите другие вопросы с метками c++ c++11 vector stl или задайте свой вопрос.
Связанные
Похожие
Подписаться на ленту
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
дизайн сайта / логотип © 2021 Stack Exchange Inc; материалы пользователей предоставляются на условиях лицензии cc by-sa. rev 2021.11.30.40849
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Как работает emplace с точки зрения памяти?
Вот определение emplace для std::vector с cppreference.com:
Вставляет новый элемент в контейнере непосредственно перед pos. Элемент построен на месте, т.е. не копировать или перемещать операции. Конструктор элемента вызывается с std::forward(args). аргументы. Тип элемента должно быть EmplaceConstructible, MoveInsertable and MoveAssignable.
Если новый size() больше, чем capacity(), все итераторы и указатели становятся нерабочими. В противном случае, нерабочими становятся только итераторы и указатели на элементы, идущие после вставленных.
Итераторы и указатели остаются в рабочем состоянии.
pos — Итератор, перед которой новый элемент будет построен
args — Аргументы направить в конструкторе элемента Оригинал:
Как я понимаю, vector – это непрерывный буфер(выделенный участок) в памяти, у которого мы можем получить значения size и capacity.
Size – возвращает количество элементов в контейнере, а capacity – общее, зарезервированное место.
Суть вопроса:
если vector – непрерывен по своей сути, то как мы можем создать новый элемент прямо на месте по позиции pos – не разрывая вектор или что еще хуже, не создавая новый vector.size +1 и не копируя все элементы в него+ новый?
Т.е если emplace производится по позиции соответствующей size и если size меньше, чем capacity, то мы просто передадим аргументы в конструктор и создадим элемент. В противном случае,если мы вставляем(создаем) объект в позиции, например соответствующей середине вектора, как я понимаю, возможен следующий вариант:
Все элементы после позиции вставляемого сдвигаем на +1 и вставляем в нужной нам позиции.
Но ведь мы должны сначала сдвинуть элементы вектора, что бы потом на освободившемя месте создать новый, и с элементами соответсвующими типу вектора – все понятно, мы знаем смещение, на которое необходимо сдвинуть объекты, но как быть с объектами производных типов?
Вот небольшой пример, иллюстрирующий, что я имею ввиду:
Вопрос касается виртуальных методов, как в таком случае будет выделяться память под объект, до его построения, в том смысле, что размер объекта из-за vpt точно известен не будет?
Т.е что будет вот в этой ситуации:
Идеальная передача и универсальные ссылки в C++
Недавно на isocpp.org была опубликована ссылка на статью Eli Bendersky «Perfect forwarding and universal references in C++». В этой небольшой статье есть простой ответ на простой вопрос — для решения каких задач и как нужно использовать rvalue-ссылки.
Одно из нововведений C++11, которое нацелено на увеличение эффективности программ – это семейство методов emplace у контейнеров STL. Например, в std::vector появился метод emplace_back (практически аналог метода push_back) и метод emplace (практически аналог метода insert).
Вот небольшой пример, показывающий предназначение этих новых методов:
Если проследить за вызовами конструкторов и деструкторов класса MyKlass, во время вызова push_back можно увидеть следующее:
Проблема идеальной передачи
Допустим, есть некоторая функция func, принимающая параметры типов E1, E2, …, En. Требуется создать функцию wrapper, принимающую такой же набор параметров. Другими словами – определить функцию, которая передаст принимаемые параметры в другую функцию, не создавая временные переменные, то есть выполнит идеальную передачу.
Для того чтобы конкретизировать задачу, рассмотрим метод emplace_back, который был описан выше. vector::emplace_back передает свои параметры конструктору T не зная ничего о том, чем является T.
Следующим шагом рассмотрим несколько примеров, показывающих как можно добиться подобного поведения без использования нововведений С++11. Для упрощения не будем учитывать необходимость использования шаблонов с переменным количеством параметров аргументов, предположим, что требуется передать только два аргумента.
Первый вариант, который приходит на ум:
Но это очевидно не будет работать как нужно, если func принимает параметры по ссылке, так как wrapper принимает параметры по значению. В этом случае, если func изменяет получаемые по ссылке параметры, это не отразится на параметрах, переданных во wrapper (будут изменены копии, созданные внутри wrapper).
Хорошо, тогда мы можем переделать wrapper, чтобы он принимал параметры по ссылке. Это не будет помехой, если func будет принимать не по ссылке, а по значению, так как func внутри wrapper сделает себе необходимые копии.
Здесь другая проблема. Rvalue не может быть передано в функцию в качестве ссылки. Таким образом вполне тривиальный вызов не скомпилируется:
И сразу нет, если пришла мысль сделать эти ссылки константными – это тоже не решит проблему. Потому что func может требовать в качестве параметров неконстантные ссылки.
Остается только грубый подход, используемый в некоторых библиотеках: перегрузить функцию для константных и не неконстантных ссылок:
Экспоненциальный рост. Можно представить, сколько веселья это доставит, когда потребуется обработать какое-то разумное количество параметров реальных функций. Чтобы ухудшить ситуацию С++11 добавляет rvalue ссылки, которые тоже нужно учесть в функции wrapper, и это точно не является расширяемым решением.
Сжатие ссылок и особый вывод типа для rvalue-ссылок
Для объяснения того, как в С++11 реализуется идеальная передача, нужно сначала понять два новых правила, которые были добавлены в этот язык программирования.
Начнем с простого – сжатия ссылок (reference collapsing). Как известно, взятие ссылки на ссылку в С++ не допускается, но это иногда может происходить при реализации шаблонов:
Что случится, если вызвать эту функцию следующим образом:
Не позволяйте двойному амперсанду обмануть Вас – t здесь не является rvalue-ссылкой [2]. При появлении в данной ситуации (когда необходим особый вывод типа), T&& принимает особое значение – когда func инстанцируется, T изменяется в зависимости от переданного типа. Если была передана lvalue типа U, то Т становится U&. Если же U это rvalue, то Т становится просто U. Пример:
Это правило может показаться необычным и даже странным. Оно такое и есть. Но, тем не менее, это правило становится вполне очевидным, когда приходит понимание что это правило помогает решить проблему идеальной передачи.
Реализация идеальной передачи с использованием std::forward
Теперь давайте вернемся к нашей описанной выше шаблонной функции wrapper. Вот как она должна быть реализована с использованием С++11:
А вот как реализован forward [3]:
Рассмотрим следующий вызов:
Рассмотрим первый аргумент (второй аналогичен): ii является lvalue, таким образом T1 становится int& в соответствии с правилом особого вывода типа. Получается вызов func(forward (e1), …). Таким образом, шаблон forward инстанцирован типом int& и получаем следующую версию этой функции:
Время применить правило сжатия ссылок:
Другими словами, аргумент передан по ссылке в func, как и требуется для lvalue.
Следующий пример:
Аргумент, полученный по ссылке, приводится к rvalue-ссылке, которую и требуется получить от forward.
Шаблонную функцию forward можно рассматривать как некоторую обертку над static_cast (t), когда T может принять значение U& или U&&, в зависимости от типа входного аргумента (lvalue или rvalue). Теперь wrapper является одним шаблоном, который обрабатывает любые сочетания типов аргументов.
Шаблонная функция forward реализована в С++11, в заголовочном файле «utility», в пространстве имен std.
Еще один момент, который нужно отметить: использование std::remove_reference. На самом деле forward может быть реализован и без использования этой функции. Сжатие ссылок выполнит всю работу, таким образом, применение std::remove_reference для этого избыточно. Однако, эта функция позволяет вывести T& t в ситуации, когда этот тип не может быть выведен (согласно стандарту С++, 14.8.2.5), поэтому необходимо явно указывать параметры шаблона при вызове std::forward.
Универсальные ссылки
В своих выступлениях, постах в блоге и книгах, Скотт Майерс дает наименование «универсальные ссылки» (universal reference) для rvalue-ссылок, которые в контексте вывода типов. Удачное это наименование или нет, сложно сказать. Что касается меня, когда я первый раз прочитал относящуюся к данной теме главу из новой книги «Effective C++», я почувствовал, что запутался. Более-менее стало все понятно позже, когда я разобрался с лежащими в основе этого механизмами (сжатия ссылок и правил особого вывода типов).
Ловушка заключается в том, что фраза «универсальные ссылки» [4] конечно более кратка и красива, чем «rvalue-ссылки в контексте вывода типов». Но если есть желание на самом деле понять некоторый код, не получится избежать полного описания.
Примеры использования идеальной передачи
Идеальная передача довольно полезна, потому что делает возможным программирование на более высоком уровне. Функции высшего порядка – это функции, которые могут принять другие функции в качестве аргументов или возвращать их. Без идеальной передачи, применение функций высшего порядка довольно обременительно, так как нет удобного способа передать аргументы в функцию внутри функции-обертки. Под термином «функция» я здесь кроме самих функций также имею в виду и классы, конструкторы которых фактически тоже являются функциями.
В начале данной статьи я описывал метод контейнеров emplace_back. Другой хороший пример – это стандартная шаблонная функция make_unique, которую я описывал в предыдущей статье:
Признаюсь честно, что в той статье я просто игнорировал странный двойной амперсанд и фокусировался на переменном количестве аргументов шаблона. Но сейчас совершенно несложно полностью понять код. Само собой разумеется, что идеальная передача и шаблоны с переменным количеством аргументов очень часто используются вместе, потому что, в большинстве случаев неизвестно, какое количество аргументов принимают функция или конструктор, которым мы передаем эти аргументы.
В качестве примера со значительно более сложным использованием идеальной передачи Вы можете посмотреть реализацию std::bind.
Ссылки на источники
От переводчика: на CppCon2014 многими (в том числе Мейерсом, Страуструпом, Саффером) было принято решение использовать термин forwarding references вместо universal references.