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

Разве это не const auto & redundant?

Я нашел этот код в одной из книг Stroustrup:

void print_book(const vector<Entry>& book)
{
     for (const auto& x : book) // for "auto" see §1.5
           cout << x << '\n';
}

Но const кажется излишним, потому что x будет выводиться как const_iterator, так как book есть const в параметре. Действительно ли const auto?

4b9b3361

Ответ 1

По-моему, это выглядит явным, и именно поэтому это лучше. Поэтому, если я имею в виду const, то я бы предпочел написать его явно. Он усиливает локальные рассуждения и, таким образом, помогает другим понять код сравнительно легко; другие программисты не должны смотреть на объявление book и заключить, что x равно const.

Ответ 2

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

В данном коде выводная константа немедленна. С предлагаемым изменением выведенная константа потребует от читателя точно запомнить или просмотреть определение book.

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

Ответ 3

Это намного понятнее ключевое слово const. Вы сразу же осознаете, что это не будет изменено, если ключевое слово const не было там, мы бы не знали, если бы не посмотрели на подпись функции.

Использование ключевого слова const явно намного лучше для читателей кода.


Кроме того, когда вы читаете код ниже, вы ожидаете, что x может быть изменен

for (auto& x : book) { ... }

И затем, когда вы пытаетесь изменить x, вы обнаружите, что это приводит к ошибке, потому что book - const.
На мой взгляд, это не ясный код. Лучше указать в коде, что x будет not изменяться, чтобы другие, которые читали код, правильно оправдали свои ожидания.

Ответ 4

Здесь я буду подходить под другим углом. const и const_iterator означают совершенно разные вещи.

const, применяемый к итератору, означает, что вы не можете изменить сам итератор, но вы можете изменить элемент, на который он указывает, подобно указателю, объявленному как int* const. const_iterator означает, что вы не можете изменить элемент, на который он указывает, но вы все равно можете изменить сам итератор (т.е. вы можете увеличивать и уменьшать его), как указатель, обозначенный как const int*.

std::vector<int> container = {1, 2, 3, 4, 5};
const std::vector<int> const_container = {6, 7, 8, 9, 0};
auto it1 = container.begin();
const auto it2 = container.begin();
auto it3 = const_container.begin();
const auto it4 = const_container.begin();

*it1 = 10; //legal
*it2 = 11; //legal
*it3 = 12; //illegal, won't compile
*it4 = 13; //illegal, won't compile

it1++; //legal
it2++; //illegal, won't compile
it3++; //legal
it4++; //illegal, won't compile

Как вы можете видеть, способность изменять элемент, на который указывает итератор, зависит только от того, является ли это iterator или const_iterator, а способность изменять сам итератор зависит только от того, объявлено ли объявление переменной для итератора имеет квалификатор const.

Изменить: я просто понял, что это диапазон, и поэтому здесь нет итераторов (по крайней мере, не явно). x не является итератором и фактически является прямой ссылкой на элемент контейнера. Но у вас есть правильная идея, будет выведено квалификатор const независимо от того, явно ли он написан.

Ответ 5

Я хотел бы предложить встречную точку. В Herb Sutter поговорите на CppCon 2014 "Вернуться к основам! Основы современного стиля С++" (временная метка 34:50), он показывает этот пример:

void f( const vector<int>& v) {
  vector<int>::iterator i = v.begin();       // error
  vector<int>::const_iterator i = v.begin(); // ok + extra thinking
  auto i = v.begin();                        // ok default

Он утверждает, что auto будет работать, даже если вы измените параметр функции, так как он правильно выводит const или non-const. Далее, на слайде 36, он заявляет: "Вы должны знать, является ли ваша переменная const/volatile или нет!" как рассуждение о том, почему "auto & &" плохо для локальных переменных.

По существу это сводится к вопросу. Некоторые считают, что быть явным хорошо для удобочитаемости или ремонтопригодности, но можно утверждать обратное: избыточность показывает отсутствие понимания (любой из правил С++ или то, что ваш код действительно делает 1), и может нанести вред читаемость и ремонтопригодность. Делайте все, что вы считаете лучшим.

1: Это довольно надуманный пример, но правила С++ на самом деле не работают в общем случае и их трудно запомнить. Например, Herb Sutter рекомендует, чтобы параметры конструктора проходили по значению, противоречащему регулярным перегрузкам функций. Это одна из тех ситуаций, когда const повсюду может укусить вас в ногу, в зависимости от того, согласны ли вы с Herb.

Ответ 6

Упоминание const рано и часто сообщает читателю кода больше о том, что происходит, если читателю не нужно смотреть дальше в коде.

Теперь, учитывая ваш код, я бы написал его как:

void print_book(const std::vector<Entry>& book)
{
  for (auto&& x : book)
     std::cout << x << '\n';
}

или даже:

void print_book(std::experimental::array_view<const Entity> book)
{
  for (auto&& x : book)
     std::cout << x << '\n';
}

но только потому, что код настолько короток, я бы опустил избыточный const.

(версия array_view устраняет бесполезную зависимость от аргумента, являющегося vector - vector<?> const&, как правило, с указанием вашего интерфейса, поскольку vector<?> const& не предоставляет ничего полезного, что array_view<?> не предоставляет, но может принудительно копировать из списков инициализации или из исходных массивов C и т.п.)

Нет причин использовать const auto& над auto& или auto&& в отношении компилятора, так как все 3 (в этом контексте) выводятся одинаковым типом. Единственная причина использовать один или другой - поговорить с другими программистами.

В моем случае я использую auto&& по умолчанию (он говорит, что я выполняю итерацию, и мне все равно, что я повторяю).

auto& Если я знаю, что мне нужно изменить, и auto const&, если я хочу подчеркнуть, что я не изменяю.

Ответ 7

этот конкретный пример довольно прост, потому что входной массив объявлен как const. В общем случае использование const в ranged-based для может быть полезно как для разработчика (он получит ошибку компилятора, если попытается изменить контейнер), так и для компилятора (явный const иногда помогает оптимизировать код)