Почему 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 );

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

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

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

2 ответа:

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

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

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

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

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

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


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

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

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

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

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

В результате,