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

Передача объекта-функтора по значению vs по ссылке (С++)

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

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

с

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

или

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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