#cpp #cpp11
В документации на cppreference указаны некоторые интересности: Для начала определения простой функции и простой структуры, они нужны для примера, как леммы в теоремах: struct Foo { Foo(int num) : num_(num) {} void print_add(int i) const { std::cout << num_+i << '\n'; } int num_; }; void print_num(int i) { std::cout << i << '\n'; } Далее идет объявление: std::functionf_display = print_num; Интересует меня параметр шаблона, я подумал: "это тип функции? ", так как еще ни разу нигде не видел круглые скобки в параметре шаблона. Но далее идет еще одна интересность: std::function f_add_display = &Foo::print_add; метод Foo::print_add принимает на вход int, что это за параметр шаблона Какой это тип? Т.е. какое общее название для таких типов? Там это не написано. И я это не могу нагуглить. Я понимаю что это объект назначения, он содержит вызов функции (указатель на метод и аргумент), но какую роль тут играют круглые скобки? Я не могу просто основываться на логике, тут нужно прочитать. Мне необязательно разжевывать, будет достаточно указать что гуглить чтобы знать об этом поподробнее, или просто дать ссылку на нужную литературу.
Ответы
Ответ 1
Как мне кажется, вопрос всё же не столько по 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: templateclass 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) (вызов свободной функции). Ответ 2
Интересует меня параметр шаблона, я подумал: "это тип функции? Это callable object, т.е. любая функция или функтор. Если мы передаем в std::function указатель на нестатическую функцию-член, то мы можем указать объект-контекст вызова, на котором эту функцию требуется вызвать. (Почти как call в JavaScript) Почитать можно здесь If the target is a pointer to a non-static member function, it is called using the first argument as the object on which the member is called (this may either be an object, a reference, or a pointer to it), and the remaining arguments are forwarded as arguments for the member function.Ответ 3
Этот тип параметра называется "функция с заданным прототипом" aka "сущность, которую можно вызвать". Англоязычный SO по той же теме То есть это вот void(int) внутри std::function- прямое указание - шаблон хочет кушать функцию с прототипом void(int). Далее. Чтобы функтор создался из нестатического метода, нужно иметь ссылку на объект и указатель на метод. Вот они: std::function f_add_display = &Foo::print_add; Первый параметр = константная ссылка на объект Foo, второй параметр - это уже первый аргумент метода Foo::print_add. Фактически, нестатический метод класса отличается от статического тем, что в него неявным образом (нулевым параметром), передается ссылка на объект, для которого мы метод вызываем. Когда мы захватываем метод в функтор, мы вместе с ним должны передать функтору и конкретный экземпляр класса. Ответ 4
Тип функции существует с момента C89. Для выражения с типом функция разрешено только две операции - конвертирование в указатель на функцию и вызов функции. typedef void(*FPtr)(int); В данном случае FPtr - тип, который есть "указатель на функцию, возвращающую void и принимающую int с соглашением по вызову её регламентированный соглашением о вызовах (calling convention) C++" Ниже пример реализации собственного std::function для глобальных функций: templatestruct SmallWrapper { public: SmallWrapper(T theFunc) : func(theFunc){} T* func; }; void test() { puts("test"); } int main() { SmallWrapper w(test); return 0' }
Комментариев нет:
Отправить комментарий