#c #c 11 #stl #return-value
#c #c 11 #stl #возвращаемое значение
Вопрос:
При использовании std::vector
s, std::list
s (или других контейнеров STL) я часто пишу это для краткости кода (вместо того vec[index]
, чтобы каждый раз указывать explicit) и эффективности распределения памяти (избегая копирования / перемещения), и я полагаю, что я не единственный, кто делает это:
std::vector<A> vec;
vec.emplace_back();
A amp;element = vec[vec.size()-1];
element.prop = "value";
Почему контейнеры STL’ emplace
emplace_back
и emplace_front
методы не возвращают a Tamp;
?Это позволило бы написать просто это, а не использовать теневой vec.size()-1
:
std::vector<A> vec;
A amp;element = vec.emplace_back();
element.prop = "value";
Комментарии:
1. Есть
vector::back()
…2. Я думаю, что наиболее вероятным обоснованием наличия
emplace_back
void
возвращаемого типа является симметрия с другими методами вставки значений, такими какpush_back
. Было бы не слишком сложно написать служебную функцию, которая выполняетemplace_back
и возвращает ссылку, которую вы ищете.3. @Deduplicator . Хороший вопрос, без понятия. Устаревшее (и совместимость) Я полагаю; возможно, об этом никогда не думали.
4. Кто-то еще, вероятно, также задается вопросом, почему вы не можете сделать:
vec.emplace_back().emplace_back()
5. Я действительно не знаю, почему они должны. Ничто в их названии не говорит мне, что они должны возвращать значение. И это не дает вам ничего с точки зрения эффективности.
Ответ №1:
Это было исправлено в C 17. Ваш пример
std::vector<A> vec;
A amp;element = vec.emplace_back();
element.prop = "value";
допустимый код C 17.
Комментарии:
1. Почему ссылка, а не итератор?
2. @akim Потому что в случае
std::vector
(-подобного) контейнераemplace_font/back()
всегда будет возвращатьfront()
/back()
, а возврат итератора потребует дополнительного косвенного обращения к помещенному объекту.3. «исправлено» находится в поле зрения наблюдателя, предполагая, что мы думали, что недостаток был ошибкой, а не функцией 😉 но я думаю, это должно быть достаточно безобидно, поскольку должно быть тривиально увидеть, что ссылка не используется, и оптимизировать ее. 1 за информацию в любом случае.
Ответ №2:
У вас есть методы-члены для доступа к этим объектам, поскольку вы знаете, куда они были вставлены. А именно front()
и back()
.
Некоторые другие функции (например map::insert
) будут возвращать итератор, потому что вы не знаете, как получить доступ к вставленному элементу за постоянное время. В случае emplace
с , вы знаете.
Другой причиной, по которой ничего не возвращается, может быть производительность (в большинстве случаев вы бы не использовали возвращаемое значение). А в C вы платите за то, что используете.
Комментарии:
1. Вероятность оптимизации компилятором
emplace_back
того, что возвращаемое значение не используется, высока (поскольку вероятность того, что большая часть функции будет встроена, высока, и компилятор может просто отбросить мертвый код, связанный с возвратом). С другой стороны, вероятность того, что компилятор оптимизирует anemplace_back()
, за которым следует aback()
, довольно низка, поскольку ему потребуется оптимизировать две разные функции и «заглянуть» в динамически выделяемый для хранения и иметь возможность отслеживать значениеsize
элемента и видеть, как осуществляется доступ к только что вставленному элементу.2. Это еще более осложняется тем фактом, что
emplace
поток имеет обычно не выровненный медленный путь «роста вектора», что в значительной степени означает, что вероятность оптимизации для этих двух функций близка к нулю, даже если путь роста обычно не встречается.
Ответ №3:
Вам это не нужно. Напишите это:
template<class C, class...Args>
auto emplace_back(Camp; c, Argsamp;amp;...args)->decltype(c.back()){
c.emplace_back(std::forward<Args>(args)...);
return c.back();
}
и у вас есть семантика, которую вы хотите, без необходимости изменять интерфейс контейнера.
Просто:
emplace_back(vec).prop = "foo";
Ответ №4:
Два аргумента для выбора этой подписи:
-
Симметрия API. Эти API-интерфейсы симметричны pop_back, pop_front и push и pop, реализованным для очередей. У этих функций (pop-функций) возникает ситуация, когда элемент может быть потерян при наличии исключения (т. Е. Элемент удаляется из коллекции, но перед его возвращением возникает исключение (например, если конструктор объекта может выдавать).).
Реализуя эту функциональность (элемент чтения и элемент pop) как две отдельные функции, обе могут быть реализованы транзакционно.
-
SRP. Хорошим руководством по дизайну является то, что если вы описываете поведение функции и вам нужно использовать слово «и», вы нарушили SRP и, вероятно, должны разделить его на две части (т. Е. Функция, которая «добавляет элемент в конце и возвращает ссылку на него», вероятно, должна быть написанакак две функции «добавить элемент в конце» и «вернуть элемент в конце» — обе из которых могут предложить по крайней мере слабые гарантии исключения для клиентского кода).
Я не уверен, применялись ли эти критерии для проектирования, но я помню аргумент гарантии исключения, приведенный в лекции по безопасности исключений.
Комментарии:
1. Помните, что в общих структурах данных последовательность двух действий «добавить элемент в конце» и «вернуть элемент в конце» отличается от одиночного действия «добавляет элемент в конец и возвращает ссылку на этот вновь добавленный элемент».
Ответ №5:
в c 11 вы можете использовать:
m.emplace(val1, val2).first
чтобы получить ссылку на возвращаемый итератор, затем:
m.emplace(val1, val2).first->first
и
m.emplace(val1, val2).first->second
для доступа к k и v карты
🙂