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


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

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

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

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

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


обновление Два примера, которые я нашел в моем избранное:

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

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

3 87

3 ответа:

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

#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&& станет оптимальным решением.

используя auto&& или универсальный ссылок с диапазоном на основе for-петля имеет то преимущество, что вы захватывает то, что вы получаете. Для большинства видов итераторов вы, вероятно, получите либо 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() означает, что вы принимаете любые возвращенные значения для перемещения. Имеет ли такой объект большой смысл в не шаблонный код я не знаю (пока?). Я могу себе представить, что использование универсальных ссылок может предложить компилятору больше информации, чтобы сделать правильную вещь. В шаблонном коде он остается вне принятия какого-либо решения о том, что должно произойти с объектами.

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