Цикл на основі діапазону C ++ 11: отримання елемента за значенням або посиланням на const


215

Читаючи деякі приклади циклів на основі діапазону, вони пропонують два основні способи 1 , 2 , 3 , 4

std::vector<MyClass> vec;

for (auto &x : vec)
{
  // x is a reference to an item of vec
  // We can change vec's items by changing x 
}

або

for (auto x : vec)
{
  // Value of x is copied from an item of vec
  // We can not change vec's items by changing x
}

Ну.

Коли нам не потрібні зміни vecелементів, IMO, Приклади пропонують використовувати другу версію (за значенням). Чому вони не пропонують щось, на що constпосилаються (Принаймні, я не знайшов жодної прямої пропозиції):

for (auto const &x : vec) // <-- see const keyword
{
  // x is a reference to an const item of vec
  // We can not change vec's items by changing x 
}

Чи не краще? Хіба це не уникає зайвої копії в кожній ітерації, поки вона є const?

Відповіді:


390

Якщо ви не хочете , щоб змінити елементи, а також хочуть , щоб уникнути створення копій, то auto const &це правильний вибір:

for (auto const &x : vec)

Той, хто пропонує вам скористатися auto &, помиляється. Ігноруйте їх.

Ось резюме:

  • Вибирайте, auto xколи ви хочете працювати з копіями.
  • Вибирайте, auto &xколи ви хочете працювати з оригінальними предметами, і можете їх модифікувати.
  • Вибирайте, auto const &xколи ви хочете працювати з оригінальними предметами, і не будете їх змінювати.

21
Дякую за чудову відповідь. Я думаю, слід також зазначити, що const auto &xеквівалентно вашому третьому вибору.
smg

7
@mloskot: Це рівнозначно. (а що ви маєте на увазі під "але це той самий синтаксис" ? Синтаксис помітно інший.)
Наваз,

14
Відсутнє: auto&&коли ви не хочете робити непотрібну копію, і вам не байдуже, ви модифікуєте її чи ні, і хочете просто працювати.
Якк - Адам Невраумон

4
Чи застосовується відмінність посилання на копії, якщо ви просто працюєте з основними типами, такими як int / double?

4
@racarate: Я не можу коментувати загальну швидкість, і я думаю, що ніхто не може, не сформулювавши її спочатку. Чесно кажучи, я не буду базувати свій вибір на швидкості, а не на чіткості коду. Якщо я хочу незмінність, я б constточно використовував . Однак, було б це auto const &, чи auto const має незначну різницю. Я хотів би auto const &просто бути більш послідовним.
Наваз

23

Якщо у вас є std::vector<int>або std::vector<double>, то autoзамість нього (із копією значення) просто прекрасно використовувати const auto&, оскільки копіювання intабо або doubleкоштує дешево:

for (auto x : vec)
    ....

Але якщо у вас є std::vector<MyClass>, де MyClassє якась нетривіальна семантика копіювання (наприклад std::string, якийсь складний спеціальний клас тощо), то я б запропонував використовувати, const auto&щоб уникнути глибоких копій :

for (const auto & x : vec)
    ....

3

Коли нам не потрібні зміни vecелементів, приклади пропонують використовувати першу версію.

Тоді вони дають неправильну пропозицію.

Чому вони не пропонують щось, що є посиланням

Тому що вони дають неправильну пропозицію :-) Те, що ви згадуєте, є правильним. Якщо ви хочете спостерігати лише за об'єктом, немає необхідності створювати його копію, і немає constпосилання на неї.

Редагувати:

Я бачу, що посилання, з якими ви посилаєтесь, наводять приклади ітерації за діапазоном intзначень або іншим основним типом даних. У такому випадку, оскільки копіювання копії не intє дорогим, створення копії в основному еквівалентне (якщо не більш ефективним, ніж) спостереженню const &.

Однак це взагалі не стосується визначених користувачем типів. Копіювання UDT може бути дорогим, і якщо у вас немає причин для створення копії (наприклад, змінити отриманий об'єкт без зміни оригіналу), то краще використовувати const &.


1

Я буду суперечити тут і скажу, що немає необхідності auto const &в діапазоні, заснованому на циклі. Скажіть, якщо ви вважаєте, що наступна функція нерозумна (не за своїм призначенням, а за тим, як вона написана):

long long SafePop(std::vector<uint32_t>& v)
{
    auto const& cv = v;
    long long n = -1;
    if (!cv.empty())
    {
        n = cv.back();
        v.pop_back();
    }
    return n;
}

Тут автор створив const посилання на vвикористання для всіх операцій, які не змінюють v. Це, на мій погляд, нерозумно, і такий же аргумент можна зробити для використання auto const &як змінної в діапазоні, заснованому на циклі, а не просто auto &.


3
@BenjaminLindley: Тож я можу зробити висновок, що ви також будете сперечатися проти const_iteratorциклу for? Як ви гарантуєте, що не змінюєте оригінальні елементи з контейнера, коли переглядаєте його?
Наваз

2
@BenjaminLindley: Чому ваш код не складеться без const_iterator? Дозвольте здогадатися, контейнер, над яким ви ітератор, є const. Але з чого це constпочинати? Десь ви використовуєте, constщоб забезпечити що?
Наваз

8
Перевага створення об’єктів const- це як переваги використання private/ protectedна заняттях. Це дозволяє уникнути подальших помилок.
масуд

2
@BenjaminLindley: О, я це не помітив. Тоді і в цьому випадку реалізація нерозумна. Але якби ця функція не змінилася v, реалізація IMO була б чудовою. І якщо цикл ніколи не змінює об'єкти, через які він повторюється, мені здається, що я маю на це посилання const. Я бачу вашу думку. Моє те, що цикл являє собою блок коду приблизно так само, як це робить функція, і всередині цього блоку немає інструкції, яка потребує зміни значення. Тому ви можете "позначати" весь блок так const, як ви робите з constфункцією-членом.
Енді Проул

1
Отже, ви вважаєте, що const &для діапазону - це нерозумно, тому що ви написали неактуальний уявний приклад уявного програміста, який зробив щось нерозумно з cv -кваліфікацією? ... Добре тоді
підкреслюй_26
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.