Тип переменной цикла for
От: Lanjeron32  
Дата: 20.01.19 19:06
Оценка:
Привет всем, у меня такой вопрос. Есть обычный vector<int> и нужно просто вывести на консоль его элементы.

vector<int> test = {1, 2, 3, 4, 5};

for (int t: test){ 
        cout << t << " ";
    }

В цикле for вместо int часто указывают auto, тогда компилятор сам определяет тип переменной t. Но оказывается, вместо int можно также указать ссылку int& — у кого-то я видел такое, и в моем примере это нормально работает. Для надежности я указал const int&, и у меня в данном примере это тоже нормально работает.
Все же, как лучше делать, в каких случаях и почему?
Re: Тип переменной цикла for
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 20.01.19 20:01
Оценка: 3 (2) +2
Здравствуйте, Lanjeron32, Вы писали:

L>В цикле for вместо int часто указывают auto, тогда компилятор сам определяет тип переменной t. Но оказывается, вместо int можно также указать ссылку int& — у кого-то я видел такое, и в моем примере это нормально работает. Для надежности я указал const int&, и у меня в данном примере это тоже нормально работает.

L>Все же, как лучше делать, в каких случаях и почему?

Если int, в переменную кладётся копия значения из вектора.

Если int&, то переменная это собственно элемент вектора. Можно при этом заменить её значение. Например, ++t в цикле — увеличит все элементы вектора на 1.

Если const int&, то тоже переменная это элемент вектора, но менять её будет нельзя.

Для int это по сути будет без разницы, если используешь t без изменения — int это просто значение, а его копирование дёшево "как 2 байта переслать" (ну, тут обычно 4, но эффект тот же).
А вот если бы это был объект, копирование которого было бы дорогой операцией — разница была бы в быстродействии.
А если копирование переносит реальное значение — то могло бы дать и разрушение сути содержимого вектора.
Поэтому для чего-то сложного — надо тщательно выбирать, какой вариант применять.

Как привычку по умолчанию для таких итераторов надо брать "const тип&", именно из-за минимизации эффектов.
А другие формы использовать только если уверен в том, что нужны именно они.
The God is real, unless declared integer.
Re[2]: Тип переменной цикла for
От: Lanjeron32  
Дата: 20.01.19 21:05
Оценка: +1
Здравствуйте, netch80, Вы писали:

N>А другие формы использовать только если уверен в том, что нужны именно они.


Спасибо за разъяснение, но хотелось бы выработать для себя практические правила, и в дальнейшем всегда следовать им в таких случаях. Мне думается, что нужно выбрать один из двух вариантов (прошу поправить, если я ошибаюсь).

1. Для vector<int> v всегда использовать for (int t: v), и не заморачиваться с const int&, поскольку выигрыша по сравнению с копированием значения практически нет. То же самое делать для float и double. Однако уже для vector<string> v всегда применять for (const string& s: v). Для сложных пользовательских типов (class, struct) — тем более.

2. Или же вообще не усложнять себе жизнь, а для этого в любых случаях всегда применять for (auto t: v).
Re[3]: Тип переменной цикла for
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 20.01.19 22:03
Оценка:
Здравствуйте, Lanjeron32, Вы писали:

N>>А другие формы использовать только если уверен в том, что нужны именно они.

L>Спасибо за разъяснение, но хотелось бы выработать для себя практические правила, и в дальнейшем всегда следовать им в таких случаях. Мне думается, что нужно выбрать один из двух вариантов (прошу поправить, если я ошибаюсь).
L>1. Для vector<int> v всегда использовать for (int t: v), и не заморачиваться с const int&, поскольку выигрыша по сравнению с копированием значения практически нет. То же самое делать для float и double. Однако уже для vector<string> v всегда применять for (const string& s: v). Для сложных пользовательских типов (class, struct) — тем более.

Почти. Не "всегда", а если нет причины делать иначе.

L>2. Или же вообще не усложнять себе жизнь, а для этого в любых случаях всегда применять for (auto t: v).


Нет. Копирования могут что-то сломать и вообще они дороги.
The God is real, unless declared integer.
Re[3]: Тип переменной цикла for
От: Don Reba Канада https://stackoverflow.com/users/49329/don-reba
Дата: 21.01.19 02:02
Оценка:
Здравствуйте, Lanjeron32, Вы писали:

L>2. Или же вообще не усложнять себе жизнь, а для этого в любых случаях всегда применять for (auto t: v).


Тут точно так же надо выбирать между auto, auto& и const auto&.
Ce n'est que pour vous dire ce que je vous dis.
Re: Тип переменной цикла for
От: LaptevVV Россия  
Дата: 21.01.19 02:55
Оценка: 2 (1)
L>
L>vector<int> test = {1, 2, 3, 4, 5};
L>for (int t: test){ 
L>        cout << t << " ";
L>    }
L>

L>Все же, как лучше делать, в каких случаях и почему?
Считай 1 элемент цикла — параметром (как в функциях). И поступай так же, как с параметрами.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[2]: Тип переменной цикла for
От: Lanjeron32  
Дата: 21.01.19 06:55
Оценка:
Здравствуйте, LaptevVV, Вы писали:

LVV>Считай 1 элемент цикла — параметром (как в функциях). И поступай так же, как с параметрами.


А если цикл будет внутри функции, которая принимает константную ссылку на вектор? Например, такая функция:

void PrintVector(const vector<string>& v) {
    for (... s: v) {
        cout << s << " ";
    }
}


На месте многоточия (…) должно стоять const string& — поскольку 1 элемент цикла надо считать параметром, как в функциях? Или здесь должно быть просто string?
Re[3]: Тип переменной цикла for
От: LaptevVV Россия  
Дата: 21.01.19 07:07
Оценка:
LVV>>Считай 1 элемент цикла — параметром (как в функциях). И поступай так же, как с параметрами.
L>А если цикл будет внутри функции, которая принимает константную ссылку на вектор? Например, такая функция:
L>
L>void PrintVector(const vector<string>& v) {
L>    for (... s: v) {
L>        cout << s << " ";
L>    }
L>} 
L>

L>На месте многоточия (…) должно стоять const string& — поскольку 1 элемент цикла надо считать параметром, как в функциях? Или здесь должно быть просто string?
Ну, сам for можно считать вызовом функции, у которой параметром является первый элемент.
В данном конкретном случае вполне подойдет const string &s
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[3]: Тип переменной цикла for
От: _vanger_  
Дата: 21.01.19 07:10
Оценка:
Здравствуйте, Lanjeron32, Вы писали:

L>
L>void PrintVector(const vector<string>& v) {
L>    for (... s: v) {
L>        cout << s << " ";
L>    }
L>} 
L>


L>На месте многоточия (…) должно стоять const string& — поскольку 1 элемент цикла надо считать параметром, как в функциях?


Да, если хочется избежать копирования. Собственно,
for (... s: v) {
    cout << s << " ";
}

более-менее эквивалентно
for (auto it = v.begin(); it != v.end(); ++it) {
    ... s = *it;
    cout << s << " ";
}

Откуда и следуют ответы на вопросы.
Re: Тип переменной цикла for
От: Igore Россия  
Дата: 21.01.19 08:01
Оценка:
Здравствуйте, Lanjeron32, Вы писали:

L>Привет всем, у меня такой вопрос. Есть обычный vector<int> и нужно просто вывести на консоль его элементы.


L>
L>vector<int> test = {1, 2, 3, 4, 5};

L>for (int t: test){ 
L>        cout << t << " ";
L>    }
L>

L>В цикле for вместо int часто указывают auto, тогда компилятор сам определяет тип переменной t. Но оказывается, вместо int можно также указать ссылку int& — у кого-то я видел такое, и в моем примере это нормально работает. Для надежности я указал const int&, и у меня в данном примере это тоже нормально работает.
L>Все же, как лучше делать, в каких случаях и почему?

Я привык так писать:
for( const auto& it: test)
{
   cout << it << " ";
}

Иногда кстати встречается еще более интересный вариант
for( const auto && it: test)
{
   cout << it << " ";
}
Re[3]: Тип переменной цикла for
От: rg45 СССР  
Дата: 21.01.19 08:25
Оценка: +3 -1
Здравствуйте, Lanjeron32, Вы писали:

L>2. Или же вообще не усложнять себе жизнь, а для этого в любых случаях всегда применять for (auto t: v).


Как раз из этих соображений — чтобы не усложнять себе жизнь — в качестве универсального варианта лучше бы выбрать "for (const auto& t : v)". Ибо урон от нежелательного копирования может быть существеенно более ощутимый, чем от лишней косвенности. С которой современые оптимизаторы справляются без особого труда.
--
Re[2]: Тип переменной цикла for
От: rg45 СССР  
Дата: 21.01.19 08:33
Оценка: 1 (1) +1
Здравствуйте, Igore, Вы писали:

I>Иногда кстати встречается еще более интересный вариант

I>
I>for( const auto && it: test)
I>{
I>   cout << it << " ";
I>}
I>


Только в этом случае "const" уже является лишним и будет только приводить к ошибкам компиляции. Вообще "auto&&" и "const auto&&" — это две большие разницы. Первый вариант — это так называемая forwarding reference (или, как ее назвали раньше "universal reference"), тогда как второй — просто константная rvalue ссылка.
--
Отредактировано 21.01.2019 8:34 rg45 . Предыдущая версия .
Re[2]: Тип переменной цикла for
От: Lanjeron32  
Дата: 21.01.19 08:43
Оценка:
Здравствуйте, Igore, Вы писали:

I>Иногда кстати встречается еще более интересный вариант

I>
I>for( const auto && it: test)
I>{
I>   cout << it << " ";
I>}
I>


Мне тоже несколько раз уже встречался такой вариант. Это для передачи по константной ссылке rvalue, правильно?

UPD: а, вижу, rg45 на этот вопрос уже ответил.
Отредактировано 21.01.2019 8:48 Lanjeron32 . Предыдущая версия .
Re[3]: Тип переменной цикла for
От: rg45 СССР  
Дата: 21.01.19 08:57
Оценка: 10 (1)
Здравствуйте, Lanjeron32, Вы писали:

L>Мне тоже несколько раз уже встречался такой вариант. Это для передачи по константной ссылке rvalue, правильно?

L>UPD: а, вижу, rg45 на этот вопрос уже ответил.

Чуть более развернуто: конструкция "auto&&" является автоподстраиваемой и может развернуться как в константную lvalue ссылку, так и в неконстантную — в зависимости от константности самого контейнера. Вот только полезность такой гибкости конкретно при пременении внутри циклов range for мне кажется сомнительной. Просто не могу придумать практически полезный пример, когда бы мы не знали, какой контейнер мы обрабатываем, константый или неконстантный.

[Upd]
Конструкция "auto&&" могла бы разворачиваться и в rvalue reference, если бы операции разыменования итераторов страндартных контейнеров возвращали либо по значению, либо rvalue ссылки. Но такого нет в стандартной библиотеке — все эти операции возвращают, by design, только lvalue ссылки — либо константные, либо некостантые. Соответственно и конструкция "auto&&" может развернуться только в lvalue ссылку, либо константную, либо неконстантную. Происходит это примерно так: допустим, операция разыменования неконстантного итератора возвращает неконстантную ссылку "int &". Тогда конструкция "auto &&" сначала разворачивается в промежуточную конструкцию вида: "int & &&". Как бы "rvalue ссылка на lvalue ссылку". Затем эта кострукция, благодаря механизму reference collapsing, появившемуся в C++11, приобретает свою финальную форму: "int&".
--
Отредактировано 21.01.2019 9:19 rg45 . Предыдущая версия .
Re[4]: Тип переменной цикла for
От: Lanjeron32  
Дата: 21.01.19 09:30
Оценка:
Здравствуйте, rg45, Вы писали:

R>Чуть более развернуто: конструкция "auto&&" является автоподстраиваемой и может развернуться как в константную lvalue ссылку, так и в неконстантную — в зависимости от константности самого контейнера. Вот только полезность такой гибкости конкретно при пременении внутри циклов range for мне кажется сомнительной. Просто не могу придумать практически полезный пример, когда бы мы не знали, какой контейнер мы обрабатываем, константый или неконстантный.

Для меня передача rvalue по ссылке, семантика move, все это пока еще довольно сложно. Мне бы сейчас разобраться с тем простым примером, где в функцию передается vector<string>, а в теле функции есть цикл for. В учебном примере я видел, сделано так:

void PrintVector(const vector<string>& v) {
    for (string s: v) {
        cout << s << " ";
    }
}

Как я понял из предыдущих ответов, в цикле for лучше вместо string поставить const string&. Но вопросы у меня еще остаются. Вектор v в функцию передается по константной ссылке, и в теле функции доступен по константной ссылке. Значит, каждый элемент s этого вектора в теле функции тоже доступен по константной ссылке, правильно? Тогда почему автор все же использует string? (между прочим, автор — ведущий разработчик Яндекса. Фамилию называть не буду, но очень сильный специалист).
Re[5]: Тип переменной цикла for
От: rg45 СССР  
Дата: 21.01.19 09:37
Оценка: 2 (1)
Здравствуйте, Lanjeron32, Вы писали:

L>Как я понял из предыдущих ответов, в цикле for лучше вместо string поставить const string&. Но вопросы у меня еще остаются. Вектор v в функцию передается по константной ссылке, и в теле функции доступен по константной ссылке. Значит, каждый элемент s этого вектора в теле функции тоже доступен по константной ссылке, правильно?


Все верно.

L>Тогда почему автор все же использует string? (между прочим, автор — ведущий разработчик Яндекса. Фамилию называть не буду, но очень сильный специалист).


Как я полагаю, просто для упрощения текста примеров. Для учебных примеров такие вопросы оптимизации не являются существенными. Тем более с учетом того, что многие реализации std::string используют технологию "copy on write". Хотя эффект получается прямо противоположный ожидаемому — вместо упрощения у читателей появляются вопросы, вполне закономерные.
--
Re[5]: Тип переменной цикла for
От: B0FEE664  
Дата: 21.01.19 10:25
Оценка:
Здравствуйте, Lanjeron32, Вы писали:

L>
L>void PrintVector(const vector<string>& v) {
L>    for (string s: v) {
L>        cout << s << " ";
L>    }
L>}
L>

L>Как я понял из предыдущих ответов, в цикле for лучше вместо string поставить const string&.

В общем случае всё ещё может зависеть от того, что потом делают с переменной s.

void PrintVector(const vector<string>& v)
{
    for (string s: v)
    {
        //заменяем все '-' на '+' 
        std::replace(s.begin(), s.end(), '-', '+');
        cout << s << " ";
    }
}
И каждый день — без права на ошибку...
Re[4]: Тип переменной цикла for
От: B0FEE664  
Дата: 21.01.19 10:58
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>Конструкция "auto&&" могла бы разворачиваться и в rvalue reference, если бы операции разыменования итераторов страндартных контейнеров возвращали либо по значению, либо rvalue ссылки. Но такого нет в стандартной библиотеке — все эти операции возвращают, by design, только lvalue ссылки — либо константные, либо некостантые.


Есть. Есть распространённый пример: std::vector<bool> для которого ссылки не компилируются:
template<typename Range, typename Value>
void set_to(Range& range, const Value& value) 
{
    for (auto& x : range)
    { 
        x = value;
    }
}


а не константный цикл изменяет сам вектор:
https://ideone.com/5yoWll

template<typename Range, typename Value>
void set_to(Range& range, const Value& value) 
{
    for (auto x : range)
    { 
        x = value;
    }
}
И каждый день — без права на ошибку...
Re: Тип переменной цикла for
От: lpd Черногория  
Дата: 21.01.19 11:10
Оценка: -2
Здравствуйте, Lanjeron32, Вы писали:

L>Привет всем, у меня такой вопрос. Есть обычный vector<int> и нужно просто вывести на консоль его элементы.

L>Все же, как лучше делать, в каких случаях и почему?

Советую сначала изучать ассемблер, как минимум чтобы представлять что происходит когда исполняется код на C++. И только потом уже заморачиваться на последние стандарты C++, если захочешь.
У сложных вещей обычно есть и хорошие, и плохие аспекты.
Берегите Родину, мать вашу. (ДДТ)
Re: Тип переменной цикла for
От: sergii.p  
Дата: 21.01.19 11:42
Оценка: :)
Здравствуйте, Lanjeron32, Вы писали:

L>Все же, как лучше делать, в каких случаях и почему?


во всех случаях auto&&. Главный аргумент — думать не надо.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.