Страницы

Поиск по вопросам

четверг, 12 декабря 2019 г.

std::next vs std::advance

#cpp


Почему std::next дефолтно продвигает на 1, а std::advance - нет? 
    


Ответы

Ответ 1



Я уже делал такое предложение комитету стандартизации по C++. Я описал данное несоответствие на своем форуме в теме std::advance и std::bitset - два простых предложения по стандарту C++ Номер этого предложения №4369. К сожалению я не отслеживал судьбу этого предложения. Формально предварительно в обсуждении оно было одобрено, но, как всегда, в комитете есть люди которые считают лишь собственные предложения самыми важными и ревниво относятся к другим предложениям. Поэтому я сейчас не в курсе, какова судьба моего предложения. Но явных причин не делать аргумент по умолчанию для второго параметра я не вижу и не встречал какие-либо серьезные возражения со стороны других оппонентов.

Ответ 2



Эти функции служат несколько разным целям и появились по совершенно разным причинам. std::advance - функция старая (С++98), которая предназначалась для унификации итераторов разных категорий в generic алгоритмах, и в первую очередь в тех ситуациях, когда итератор надо отодвинуть более чем на один шаг. С отодвиганием ровно на один шаг справлялись операторы ++ и --. std::advance была выполнена именно в виде именованной функции (а не перегрузки операторов += или -=) именно для того, чтобы привлечь внимание пользователя к потенциально неэффективной операции и тем самым постараться исключить ее непреднамеренное использование. Аналогичные причины обусловили появление "парной" явной функции std::distance. std::next и std::prev - функции новые (С++11), созданные в частности для того, чтобы инкапсулировать идиому получения соседнего итератора без модификации текущего. Учитывая, что в общем случае к итератору не применима бинарная операция +, нельзя просто взять и прибавить к итератору 1 It it; ... foo(it + 1); // в общем случае - не сработает Для итераторов класс-типов в такой ситуации работает компактный вариант foo(++It(it)); но он неприменим к итераторам фундаментальных типов, что в generic контекстах заставляет заводить дополнительную именованную переменную, т.е. выписывать что-то вроде It it_next = it; foo(++it_next); Это более громоздко, чем хотелось бы, и вводит в код ненужную именованную переменную. Функция std::advance, если обратить внимание на особенности ее интерфейса, тут никак помочь нам не может. Вот тут-то и приходят на помощь функции std::next и std::prev, которые скрывают подобные детали. При этом их наиболее востребованным назначением/употреблением является именно получение соседнего итератора. А возможность указать расстояние - лишь естественное расширение этой функциональности. Другими словами, ответ на ваш вопрос в историческом ключе звучит просто: std::advance была создана именно для сдвига итератора более чем на 1 шаг, а std::next и std::prev заведены в первую очередь для сдвига ровно на 1 шаг. Также учитывая, что std::advance может работать в обоих направлениях, было бы странно сейчас навязывать ей именно +1 в качестве умолчательного аргумента. Вполне может быть, что если бы изначально в C++98 функцию std::advance ввели именно с интерфейсом template< class InputIt, class Distance > InputIt advance( InputIt it, Distance n ); (т.е. с передачей и возвратом итератора "по значению", а не с передачей модифицируемого итератора "по ссылке"), то сегодня особой необходимости в std::next и std::prev так и не возникло бы. А если бы эти функции и появились бы, то возможно без параметра "расстояния".

Ответ 3



Почему std::next дефолтно продвигает на 1, а std::advance - нет? Потому как std::next как и std::prev явно указывают направление модификации. И наличие дефолтного значения - лишь уточнение шага. Приписать же дефолтную модификацию для std::advance смысла нет, так как для данной функции позитивное и негативное смещения "равно-значимы". Если прописать шаг, то это автоматически приравнивается и к прописке дефолтного направления модификации, что не будет соответствовать аббревиатуре функции.

Комментариев нет:

Отправить комментарий