В чем преимущество использования универсальных ссылок в циклах для диапазонов?

const auto& было бы достаточно, если я хочу выполнять операции только для чтения. Тем не менее, я столкнулся с

for (auto&& e : v)  // v is non-const

пару раз недавно. Это заставляет меня задуматься:

Возможно ли, что в некоторых неясных угловых случаях есть некоторая выгода в использовании универсальных ссылок по сравнению с auto& или же const auto&?

(shared_ptr подозревается в неясных угловых делах)


Обновить
Два примера, которые я нашел в моих любимых:

Любой недостаток использования константной ссылки при переборе основных типов?
Могу ли я легко перебрать значения карты, используя основанный на диапазоне цикл for?

Пожалуйста, сконцентрируйтесь на вопросе: почему я хочу использовать авто&& на основе диапазона для петель?

91

Решение

Единственное преимущество, которое я вижу, это когда итератор последовательности возвращает ссылку на прокси, и вам нужно работать с этой ссылкой неконстантным способом. Например, рассмотрим:

#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;
}

редактировать

Этот мой последний случай действительно должен иметь смысл. Если вы знаете, что цикл всегда обрабатывает ссылку на прокси, то auto будет работать так же, как auto&&, Но когда цикл иногда обрабатывает непрокси-ссылки, а иногда и прокси-ссылки, тогда я думаю, auto&& станет решением выбора.

76

Другие решения

С помощью auto&& или же универсальные ссылки с диапазоном forПреимущество -loop в том, что вы захватываете то, что получаете. Для большинства типов итераторов вы, вероятно, получите T& или T const& для какого-то типа T, Интересный случай, когда разыменование итератора приводит к временному: C ++ 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петля будет другой? Конечно, чтобы на самом деле воспользоваться выводом типа с использованием универсальных ссылок, вам необходимо передать их соответствующим образом:

for (auto&& x: range) {
f(std::forward<decltype(x)>(x));
}

Конечно, используя std::forward() означает, что вы принимаете любые возвращаемые значения для перемещения. Имеет ли смысл подобные объекты в не шаблонном коде, я не знаю (пока?). Я могу себе представить, что использование универсальных ссылок может предложить компилятору больше информации для правильного выполнения. В шаблонном коде он не принимает решения о том, что должно происходить с объектами.

25

Я практически всегда использую auto&&, Зачем укусить крайний случай, когда вам не нужно? Его тоже короче набирать, и я просто нахожу его более … прозрачным. Когда вы используете auto&& xтогда вы знаете, что x это точно *it, каждый раз.

7
По вопросам рекламы [email protected]