Одним словом, используйте std::function
, если у вас нет оснований для этого.
Указатели указателей имеют недостаток неспособность захватить некоторый контекст. Вы не сможете, например, передать лямбда-функцию в качестве обратного вызова, который захватывает некоторые переменные контекста (но он будет работать, если он не фиксирует какой-либо). Таким образом, вызов элементарной переменной объекта (т. Е. Нестатический) также невозможен, поскольку объект (-pointer) должен быть захвачен. (1)
std::function
(так как C++, 11) в первую очередь для магазин функция (передавая ее вокруг этого не требует, чтобы быть сохранены). Следовательно, если вы хотите сохранить обратный вызов, например, в переменной-члене, это, вероятно, ваш лучший выбор. Но также, если вы его не храните, это хороший «первый выбор», хотя у него есть недостаток в том, что он вводит некоторые (очень маленькие) накладные расходы при вызове (поэтому в очень критичной для производительности ситуации это может быть проблемой, но в большинстве случаев он не должен). Это очень «универсально»: если вы много заботитесь о совместимом и читаемом коде, а также не хотите думать о каждом выбранном вами выборе (т. Е. Хотите, чтобы это было просто), используйте для каждой функции, которую вы проходите, используйте std::function
.
Подумайте о том, третьем варианте: Если вы собираетесь реализовать небольшую функцию, которая затем сообщает что-то с помощью прилагаемого функции обратного вызова, рассмотрит параметр шаблона, который затем может быть любой вызываемый объектом, т.е. функции указатель, функтор, лямбда, std::function
, ... Недостатком здесь является то, что ваша (внешняя) функция становится шаблоном и, следовательно, должна быть реализована в заголовке. С другой стороны, вы получаете преимущество в том, что вызов обратного вызова может быть встроен, так как клиентский код вашей (внешней) функции «видит» вызов обратного вызова будет иметь точную информацию о типе.
Пример для версии с параметром шаблона (написать &
вместо &&
для предварительной C++ 11):
template <typename CallbackFunction>
void myFunction(..., CallbackFunction && callback) {
...
callback(...);
...
}
Как вы можете видеть в таблице ниже, все они имеют их преимущества и недостатки:
+-------------------+--------------+---------------+----------------+
| | function ptr | std::function | template param |
+===================+==============+===============+================+
| can capture | no(1) | yes | yes |
| context variables | | | |
+-------------------+--------------+---------------+----------------+
| no call overhead | yes | no | yes |
| (see comments) | | | |
+-------------------+--------------+---------------+----------------+
| can be inlined | no | no | yes |
| (see comments) | | | |
+-------------------+--------------+---------------+----------------+
| can be stored | yes | yes | no(2) |
| in class member | | | |
+-------------------+--------------+---------------+----------------+
| can be implemented| yes | yes | no |
| outside of header | | | |
+-------------------+--------------+---------------+----------------+
| supported without | yes | no(3) | yes |
| C++11 standard | | | |
+-------------------+--------------+---------------+----------------+
| nicely readable | no | yes | (yes) |
| (my opinion) | (ugly type) | | |
+-------------------+--------------+---------------+----------------+
(1) Существуют способы обхода этого ограничения, например, передача дополнительных данных в качестве дополнительных параметров для вашей (внешней) функции: myFunction(..., callback, data)
вызовет callback(data)
. Это C-стиль «обратный вызов с аргументами», который возможен в C++ (и, кстати, сильно используется в WIN32 API), но его следует избегать, потому что у нас есть лучшие варианты на C++.
(2) Если мы не говорим о шаблоне класса, то есть классе, в котором вы храните эту функцию, является шаблон. Но это будет означать, что на стороне клиента тип функции определяет тип объекта, который хранит обратный вызов, который практически никогда не является вариантом для фактических вариантов использования.
(3) Для предварительного C++ 11, используют boost::function
Если функция обратного вызова известна во время компиляции, рассмотрите вместо этого шаблон. –
Когда ** реализует ** функцию обратного вызова, вы должны делать все, что требуется вызывающему. Если ваш вопрос действительно связан с ** проектированием ** интерфейса обратного вызова, здесь нет почти достаточной информации, чтобы ответить на него. Что вы хотите, чтобы получатель вашего обратного вызова сделал? Какую информацию вам нужно передать получателю? Какая информация должна быть возвращена получателю в результате вызова? –