Почему std:: find if(first, last, p) не принимает предикат по ссылке?

Я смотрел на различные подписи для std::find_if ВКЛ cppreference.com, и я заметил, что ароматы, которые принимают функцию предиката, по-видимому, принимают ее по значению:

template< class InputIt, class UnaryPredicate >
InputIt find_if( InputIt first, InputIt last,
             UnaryPredicate p );

Если я правильно их понимаю, лямбды с захваченными переменными выделяют хранилище для ссылок или копий своих данных, и поэтому, по-видимому, "pass-by-value" будет означать, что копии захваченных данных копируются для вызова.

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

во-первых, это правильно? Это UnaryPredicate выше будет значение параметра?

во-вторых, правильно ли мое понимание прохождения лямбд?

В-третьих, есть ли причина для передачи по значению, а не по ссылке в этой ситуации? И более того, там нет достаточно неоднозначного синтаксиса (привет, универсальная ссылка), который позволил бы компилятору делать все, что он хочет, чтобы получить максимальную производительность?

2 ответов


является ли UnaryPredicate выше будет параметром по значению?

Да, вот что он говорит в списке параметров функции. Он принимает тип выведенного значения.

помимо этого, лямбда-выражения являются prvalues. Значит, с c++17гарантированная копия elision, что p инициализируется напрямую из лямбда-выражения. Никакие дополнительные копии закрытия или захваченных объектов не производятся при его передаче в функцию (функция может сделать больше копий внутри, однако, хотя это не распространено).

если предикат был передан по ссылке, необходимо материализовать временный объект. Таким образом, для лямбда-выражения коммутатор не получает ничего для передачи по ссылке.

если у вас есть другие виды предикатов, которые экспансивны для копирования, то вы можете передать std::reference_wrapper к этому объекту предиката, для дешевого "дескриптора" к нему. Обертка operator() поступлю правильно.

определение в основном исторический, но в настоящее время это действительно не проблема, чтобы сделать это с пропуском по стоимости.


чтобы уточнить, почему референтная семантика будет сосать, давайте попробуем взять его через годы. Простая ссылка lvalue не будет делать, так как теперь мы не поддерживаем привязку к rvalue. Ссылка const lvalue также не будет делать, так как теперь мы требуем, чтобы предикат не изменял никакого внутреннего состояния, и зачем?

до c++11, у нас нет альтернативы. Пропуск по значению будет лучше, чем ссылка. С новым стандартом, мы можем пересмотреть наш подход. Для поддержки rvalues мы можем добавить перегрузку ссылки rvalue. Но это упражнение в избыточности, поскольку ему не нужно делать ничего другого.

передавая значение, вызывающий имеет выбор в том, как его создать, и для prvalues, в c++17, это практически бесплатно. Если вызывающий так желает, они могут предоставить ссылочную семантику явно. Так что ничего не потеряно, и я думаю, что многое достигнуто с точки зрения простоты использования и дизайна API.


на самом деле есть несколько причин:

  1. вы всегда можете превратить выведенные аргументы значения в использование ссылочной семантики, но не вице-стих: просто передайте std::ref(x) вместо x. 'std:: reference_wrapper не полностью эквивалентен передаче ссылки, но особенно для объекта функции он делает правильную вещь. То есть передача общих аргументов по значению является более общим подходом.
  2. проходим по ссылке (T&) не работает для временного или const объекты T const& не работает для не-const&, т. е. единственным выбором будет T&& (ссылка для пересылки), которая не существовала до c++11, и интерфейсы алгоритмов не изменились с тех пор, как они были введены с C++98.
  3. параметры значения можно скопировать elided не похож на любой вид параметров справки, включая ссылки препровождения.

в результате