Передача объекта функтора по значению против ссылки (C ++) - PullRequest
18 голосов
/ 19 ноября 2011

Сравните общие интеграционные функции:

template <class F> double integrate(F integrand);

с

template <class F> double integrate(F& integrand);

или

template <class F> double integrate(const F& integrand);

Каковы плюсы и минусы каждого?STL использует первый подход (передача по значению), значит ли это, что он самый универсальный?

Ответы [ 3 ]

24 голосов
/ 19 ноября 2011

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

Если функтор не имеет состояния, передача его в качестве аргумента вообще не требует затрат - байт заполнения, который принимает функтор, не должен иметь какого-либо конкретного значения (по крайней мере, в Itanium Abi, используемом GCC). При использовании ссылок вы всегда должны передавать адрес.

Последний (const T&) имеет тот недостаток, что в C ++ 03 он не работает для необработанных функций, потому что в C ++ 03 программа некорректна, если вы пытаетесь применить const к тип функции (и это случай SFINAE). Более поздние реализации вместо этого игнорируют const при применении к типам функций.

Второй (T&) имеет очевидный недостаток: вы не можете передавать временные функторы.

Короче говоря, я бы обычно передавал их по значению, если не вижу явного преимущества в конкретных случаях.

7 голосов
/ 19 ноября 2011

STL использует первый подход (передача по значению)

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

Но это только для целей, для которых стандартные библиотеки используют функторы - в основном это предикаты, хотя есть и такие вещи, как std::transform.Если вы интегрируете функцию, которая предлагает какие-то математические библиотеки, в этом случае я полагаю, что вы, скорее всего, будете иметь дело с функциями, которые несут много состояний.Например, у вас может быть класс, представляющий полиномы n-го порядка с n + 1 коэффициентами в качестве нестатических элементов данных.

В этом случае может быть лучше использовать константную ссылку.При использовании такого функтора в стандартных алгоритмах, таких как transform, вы можете обернуть его в небольшой класс, который выполняет косвенное обращение через указатель, чтобы гарантировать, что он остается дешевым для копирования.

Взятие неконстантной ссылки - этопотенциально раздражает пользователей, так как останавливает их переход во временные промежутки.

3 голосов
/ 19 ноября 2011

Учитывая контекст, ожидается, что F будет «вызываемым объектом» (что-то вроде свободной функции или класса с определенным оператором ())

Теперь, поскольку имя свободной функции не может быть L-значением, вторая версия для этого не подходит. Третий предполагает, что F :: operator () является const (но это может быть не так, если требуется изменить состояние F) Первый работает с «собственной копией», но требует, чтобы его можно было скопировать.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...