Страницы

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

пятница, 5 октября 2018 г.

С++ 11, std::function, неясность с параметрами шаблона

В документации на cppreference указаны некоторые интересности: Для начала определения простой функции и простой структуры, они нужны для примера, как леммы в теоремах:
struct Foo { Foo(int num) : num_(num) {} void print_add(int i) const { std::cout << num_+i << '
'; } int num_; };
void print_num(int i) { std::cout << i << '
'; }
Далее идет объявление:
std::function f_display = print_num;
Интересует меня параметр шаблона, я подумал: "это тип функции? ", так как еще ни разу нигде не видел круглые скобки в параметре шаблона.
Но далее идет еще одна интересность:
std::function f_add_display = &Foo::print_add;
метод Foo::print_add принимает на вход int, что это за параметр шаблона
Какой это тип? Т.е. какое общее название для таких типов? Там это не написано. И я это не могу нагуглить. Я понимаю что это объект назначения, он содержит вызов функции (указатель на метод и аргумент), но какую роль тут играют круглые скобки? Я не могу просто основываться на логике, тут нужно прочитать.
Мне необязательно разжевывать, будет достаточно указать что гуглить чтобы знать об этом поподробнее, или просто дать ссылку на нужную литературу.


Ответ

Как мне кажется, вопрос всё же не столько по std::function, сколько по аргументу шаблона, у которого внутри используются круглые скобки.
Интересует меня параметр шаблона, я подумал: "это тип функции?"
Да. Это именно тип функции. К тому же такая запись типа разрешена с незапамятных времен. Но использовалось в таком виде не часто. Чаще приходилось наблюдать тип "указатель на функцию", заданный обычно при помощи typedef. Например:
typedef void(*FP)(int);
В данном случае FP - тип, который есть "указатель на функцию, возвращающую void и принимающую int".
Одновременно можно задать тип F - "функция, возвращающая void и принимающую int":
typedef void(F)(int); // скобки вокруг F необязательны
При этом тип F* это будет то же самое, что FP. Т.е. везде, где используется FP можно использовать F*. Например:
void func(int); FP fp1 = &func; F* fp2 = &func; fp2 = fp1;
Возвращаемся теперь к шаблонам. Для некоего шаблонного класса C
template class C { };
можно завести тип C, или, аналогичный в интересуемой нас записи с круглыми скобками C. Несложно убедиться, что эти типы эквиваленты.
С принятием c++11 форму со скобочками можно использовать и в using
using F2 = void(int);
Тип F2 будет эквивалентен типу F

Скажу несколько слов по поводу второго примера из вопроса, т.е. уже относительно std::function.
Тип выражения &Foo::print_add есть void(Foo::*)(int) (указатель на функцию-член) и в явном виде он не может быть преобразован к void(const Foo&, int) (указатель на свободную функцию), который задан в качестве шаблонного типа. Однако реализация std::function допускает такое преобразование (это регламентировано Стандартом (п.20.12.2 черновика)), и т.о. справа от = может стоять как указатель на упомянутую функцию-член, так и на свободную функцию вида void FreeFunction(const Foo&, int). В данном случае определение INVOKE (которое присутствует в описании std::function) разворачивается либо в (t1.*f)(t2, ..., tN) (вызов функции-члена), либо в f(t1, t2, ..., tN) (вызов свободной функции).

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

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