Подтвердить что ты не робот

С++ 11 Эффективность цикла для контура "const auto & i" по сравнению с "auto i"

В С++ 11 я могу перебирать несколько контейнеров:

for(auto i : vec){
   std::cout << i << std::endl;
}

Но я знаю, что это бесполезно - бесполезно, так как мне нужно только распечатать значения vec - делает копию ( EDIT: каждый элемент) vec, поэтому вместо этого я может сделать:

for(auto &i : vec){
   std::cout << i << std::endl;
}

Но я хочу удостовериться, что значения vec никогда не изменяются и не соблюдают const-correctness, поэтому я могу сделать:

for(const auto &i : vec){
   std::cout << i << std::endl;
}

Итак, мой вопрос: если мне нужно только посмотреть на значения какого-либо контейнера, не всегда ли был бы самый последний цикл (const auto &i) из-за повышенной эффективности отсутствия дополнительной копии ( EDIT: каждый элемент) vec?

У меня есть программа, которую я разрабатываю, в которой я планирую сделать это изменение повсюду, поскольку эффективность в ней важна (причина, по которой я использую С++ в первом месте).

4b9b3361

Ответ 1

Да. По той же причине, если вы только когда-либо читали аргумент, вы делаете параметр const&.

T        // I'm copying this
T&       // I'm modifying this
const T& // I'm reading this

Это ваши "значения по умолчанию". Однако, если T является фундаментальным типом (встроенным), вы обычно просто возвращаетесь к const T (без ссылки) для чтения, потому что копия дешевле, чем наложение псевдонимов.


У меня есть программа, которую я разрабатываю, в которой я планирую сделать это изменение повсюду, поскольку эффективность в ней важна

  • Не делайте слепых изменений. Рабочая программа лучше быстрой, но сломанной программы.
  • Как вы повторяете свои петли, вероятно, не будет иметь большого значения; вы зацикливаетесь по какой-то причине, не так ли? Тело вашей петли, скорее всего, станет виновником.
  • Если эффективность важна, вы хотите использовать профилировщик, чтобы найти, какие части вашей программы на самом деле медленны, а не гадать на части, которые могут быть медленными. См. № 2, почему ваша догадка может быть неправильной.

Ответ 2

Представьте, если ваш вектор содержит строки. Длинные струны. 5000 длинных струн. Скопируйте их без необходимости, и вы получите красиво написанный цикл, который ужасно неэффективен.

Убедитесь, что ваш код соответствует вашим намерениям. Если вам не нужна копия внутри цикла, не делайте ее.

Используйте ссылку и, как было предложено выше, или итераторы.

Ответ 3

Отказ от ответственности: В целом разница между auto и auto& является тонкой, отчасти вопросом стиля, но иногда также вопросом правильности. Я не собираюсь освещать здесь общий случай!

В диапазоне, основанном на цикле, разница между

for (auto element : container) {}

и

for (auto& element_ref : container) {}

означает, что element является копией элементов в container, а element_ref является ссылкой на элементы в контейнере.

Чтобы увидеть разницу в действии, рассмотрите этот пример:

#include <iostream>

int main(void) {
    int a[5] = { 23,443,16,49,66 };

    for (auto i : a) i = 5;       
    for (const auto& i : a) std::cout << i << std::endl;
    for (auto& i : a) i = 5;   
    for (const auto& i : a) std::cout << i << std::endl;    
}

Он напечатает

23
443
16
49
66
5
5
5
5
5

потому что первый цикл работает с копиями элементов массива, а второй фактически изменяет элементы в массиве.

Если вы не хотите изменять элементы, то часто const auto& более уместен, потому что он избегает копирования элементов (что может быть дорогостоящим).