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

Лучший способ передачи параметров функции обратного вызова в С++

Каков наилучший способ передачи параметра функции обратного вызова в С++?

Я думал просто использовать шаблоны, например:

template <typename Function>
void DoSomething(Function callback)

Это способ, используемый, например, в std::sort для объекта функции сравнения.

Как насчет передачи с помощью &&? Например:.

template <typename Function>
void DoSomething(Function&& callback)

Каковы плюсы и минусы этих двух методов, и почему STL использует первое, например, в std::sort?

4b9b3361

Ответ 1

template <typename Function>
void DoSomething(Function&& callback)

& hellip; который использует справочную ссылку для передачи по ссылке, является IMHO превосходным для случая, когда функция просто использует обратный вызов. Поскольку объект-функтор, хотя обычно и мал, может быть сколь угодно большим. Передавая его по значению, тогда возникают некоторые ненужные накладные расходы.

Формальный аргумент может заканчиваться как T&, T const& или T&& в зависимости от фактического аргумента.


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

Если у вас есть сомнения, важно ли это для данного компилятора, системы и приложения, измерить.


Относительно

", почему STL использует предыдущий [переход по значению], например, в std::sort?

& hellip; стоит отметить, что std::sort существует с С++ 98, задолго до того, как в С++ 11 были добавлены ссылки на пересылку, поэтому она не могла первоначально иметь эту подпись.

Возможно, просто не было достаточных стимулов для улучшения этого. В конце концов, как правило, не следует устанавливать то, что работает. Тем не менее, дополнительные возможности с параметром политика выполнения представлены на С++ 17.

Так как это касается возможного изменения С++ 11, оно не покрывается обычным источником для обоснований, а именно Bjarne Stroustrup "Дизайн и эволюция С++" ., и я не знаю никакого окончательного ответа.


Используя стиль template<class F> void call( F ), вы все равно можете вернуться "пройти по ссылке". Просто сделайте call( std::ref( your callback ) ). std::ref переопределяет operator() и перенаправляет его на содержащийся объект.

Симпатично, используя стиль template<class F> void call( F&& ), вы можете написать:

template<class T>
std::decay_t<T> copy( T&& t ) { return std::forward<T>(t); }

который явно что-то копирует, и принудительно call использовать локальную копию f по:

call( copy(f) );

поэтому два стиля в основном отличаются тем, как они себя ведут по умолчанию.


Ответ 2

Так как это С++ 11 (или выше): <functional> и std::function являются вашими лучшими друзьями здесь.

#include <iostream>
#include <functional>
#include <string>


void DoSomething(std::function<void()> callback) {
    callback();
}

void PrintSomething() {
   std::cout << "Hello!" << std::endl;
}

int main()
{
    DoSomething(PrintSomething);
    DoSomething([]() { std::cout << "Hello again!" << std::endl; });
}