Страницы

Поиск по вопросам

четверг, 2 января 2020 г.

Способы передачи функции(функтора) в качестве аргумента другой функции

#cpp #cpp11


Наткнулся тут на вопрос про std::bind и возник вопрос.

Чем отличается способ с шаблоном

template 
void display( int a[], size_t n, Operation op ) // или Operation&&
{    
    for ( size_t i = 0; i < n; i++ )
    {        
        std::cout << op( a[i] ) << ' ';
    }       
    std::cout << std::endl;
}


от 

void display( int a[], size_t n, std::function operation )
{    
    //...
}


и от

void display( int a[], size_t n, int operation( int ) )
{    

}


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


Ответы

Ответ 1



Первый вариант принимает любой объект, не проверяя его тип. Так, если туда передать что-то, что не может быть вызвано, или что-то, что возвращает тип, который не может быть использован в cout, то получите не совсем релевантную ошибку компиляции. Второй вариант принимает любой объект, который может быть вызван. При это согласованность сигнатур проверяется сразу и выдаётся релевантная ошибка, что позволяет быстрее понять, где проблема. Третий вариант принимает лишь свободные функции и лямбды без состояний и является более ограниченным чем второй вариант. Использовать третий вариант в современном C++ коде нет никакого смысла. Касательно применения: стоит всегда использовать std::function, за исключением тех моментов, когда это по каким-то причинам вредит.

Ответ 2



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

Ответ 3



Если переданная функция вызывается в цикле, то предпочтительнее передавать ее как шаблонный параметр, чтобы компилятор мог ее заинлайнить. Обычно функторы - это небольшие объекты, и их можно передавать по значению. Если возможности для инлайна нет (например переданная функция кладется в list>), то можно сразу использовать параметр с типом function. Указатель на функцию следует использовать только если всегда будет передаваться и храниться именно указатель на функцию. std::function<> имеет размер около 4 указателей (зависит от реализации), и иногда хочется избежать x4 расходов на хранение и копирование, если известно что надо хранить только указатели на функции.

Комментариев нет:

Отправить комментарий