Страницы

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

суббота, 30 ноября 2019 г.

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

#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::function f_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: 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) (вызов свободной функции).

Ответ 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 для глобальных функций: template struct SmallWrapper { public: SmallWrapper(T theFunc) : func(theFunc){} T* func; }; void test() { puts("test"); } int main() { SmallWrapper w(test); return 0' }

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

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