В чем преимущество использования ссылок пересылки в циклах для диапазонов?
const auto&
будет достаточно, если я хочу выполнять операции только для чтения. Тем не менее, я столкнулся с
for (auto&& e : v) // v is non-const
пару раз недавно. Это заставляет меня задуматься:
Возможно ли, что в некоторых непонятных угловых случаях есть преимущество в производительности при использовании ссылок пересылки по сравнению с auto&
или const auto&
?
(shared_ptr
является подозреваемым для неясных угловых случаев)
Обновление Два примера, которые я нашел в моих любимых:
Любой недостаток использования константной ссылки при переборе основных типов?
Могу ли я легко перебрать значения карты, используя основанный на диапазоне цикл for?
Пожалуйста, сконцентрируйтесь на вопросе: зачем мне использовать auto && в циклах for на основе диапазона?
Ответы
Ответ 1
Единственное преимущество, которое я вижу, - это когда итератор последовательности возвращает ссылку на прокси-сервер, и вам нужно работать с этой ссылкой неконстантным способом. Например, рассмотрим:
#include <vector>
int main()
{
std::vector<bool> v(10);
for (auto& e : v)
e = true;
}
Это не компилируется, потому что rvalue vector<bool>::reference
, возвращенный из iterator
, не будет связываться с ссылкой на константу lvalue. Но это будет работать:
#include <vector>
int main()
{
std::vector<bool> v(10);
for (auto&& e : v)
e = true;
}
Все, что было сказано, я бы не закодировал этот путь, если вы не знали, что вам нужно удовлетворить такой прецедент. То есть Я не сделал бы это безвозмездно, потому что это заставляет людей задаваться вопросом, что вы делаете. И если бы я это сделал, не помешало бы включить комментарий о том, почему:
#include <vector>
int main()
{
std::vector<bool> v(10);
// using auto&& so that I can handle the rvalue reference
// returned for the vector<bool> case
for (auto&& e : v)
e = true;
}
Edit
Этот последний мой случай действительно должен быть шаблоном, чтобы иметь смысл. Если вы знаете, что цикл всегда обрабатывает ссылку на прокси-сервер, тогда auto
будет работать так же, как и auto&&
. Но когда цикл иногда обрабатывал ссылки без прокси-сервера, а иногда и прокси-ссылки, я думаю, что auto&&
станет решением выбора.
Ответ 2
Использование auto&&
или универсальных ссылок с диапазоном for
-loop имеет то преимущество, что вы фиксируете то, что получаете. Для большинства типов итераторов вы, вероятно, получите либо T&
, либо T const&
для некоторого типа T
. Интересным случаем является то, что разыменование итератора приводит к временному: С++ 2011 получил расслабленные требования, и итераторы не обязательно должны давать lvalue. Использование универсальных ссылок соответствует пересылке аргументов в std::for_each()
:
template <typename InIt, typename F>
F std::for_each(InIt it, InIt end, F f) {
for (; it != end; ++it) {
f(*it); // <---------------------- here
}
return f;
}
Объект функции f
может обрабатывать T&
, T const&
и T
по-разному. Зачем нужно, чтобы тело диапазона for
-loop отличалось? Конечно, чтобы на самом деле воспользоваться выводом типа с использованием универсальных ссылок, вам нужно было бы их соответствующим образом передать:
for (auto&& x: range) {
f(std::forward<decltype(x)>(x));
}
Конечно, использование std::forward()
означает, что вы принимаете любые возвращаемые значения, которые нужно перенести из. Являются ли такие объекты похожими на код без шаблона, я не знаю (пока?). Я могу себе представить, что использование универсальных ссылок может предоставить компилятору больше информации о том, как делать правильную вещь. В шаблоном коде он не принимает решения о том, что должно произойти с объектами.
Ответ 3
Я практически всегда использую auto&&
. Зачем укусить кромку, когда вам это не нужно? Короче печатать, и я просто нахожу его более... прозрачным. Когда вы используете auto&& x
, вы знаете, что x
точно *it
, каждый раз.