Являются ли указатели функций функциональными объектами в C++?
Стандарт C++ определяет функциональные объекты как:
Тип объекта функции - это тип объекта, который может быть типом выражения postfix в вызове функции. ( ссылка)
Сначала я думал, что функциональные объекты являются функторами, но потом я понял, что для указателя на функцию ptr
типа P
(не функция, а указатель на функцию), std::is_object_v<P>
является true
и может быть вызван с ptr(Args...)
синтаксис.
Я прав, что указатели на функции в стандарте рассматриваются как функциональные объекты? А если нет, то какая часть определения не удовлетворяется указателями на функции?
3 ответа
Да. Термин "объект" в стандарте C++ не означает "объект" в смысле ООП. int
это объект.
Указатель на функцию - это то, на что это похоже: указатель на функцию. Само по себе это хранилище, содержащее объект-указатель, который возвращает вызываемый тип функции.
Если вы потратите время и прочитаете первые главы стандарта, вы поймете, что любое объявление переменной объявляет некоторый тип хранилища, которое содержит объекты. Это могут быть объекты примитивных типов или классов. По сути, в C++ все, что может быть сохранено, является объектом.
Объявляя указатель на функцию, вы создаете хранилище, в котором можно хранить адрес этой функции, и можно использовать operator()
Другой тип вызываемого замыкания может быть создан с помощью лямбда-выражения. Они не являются функциональными объектами, каждое выражение создает уникальный вызываемый объект, но лямбды без захвата можно использовать как единое целое, например, для назначения их указателю на функцию, например
double (*square)(double) = [](double a)->double { return a*a; };
после этого вы можете назвать это с помощью выражения, как square(3.6);
Для функций и лямбда-оператора вызова operator()
обеспечивается языком, определяя operator()
для класса вы создаете то, что люди часто называют "функтором", что неправильно, потому что фактические функторы в математике или в таких языках, как Haskell , не сохраняют состояние. Результатом лямбда-выражения является "функтор", созданный компилятором, который хранит состояния захваченных объектов.
Именование этих вызываемых объектов также может вводить в заблуждение, поскольку в качестве концепции в C++ вызываемый объект - это любой объект, который можно использовать с операцией INVOKE, которая включает в себя указатели на члены данных, даже когда не происходит вызовов функций.
он оставляет только одну опцию, если мы можем использовать вызов функции с указанным объектом, это объект функции. Это может быть функция, лямбда-выражение, объект функции, указатель на функцию, указатель на функцию-член с указанным экземпляром класса (obj.*memberptr
или же objptr->*memberptr
- вызов функции-члена очень особенный) - они являются функциональными объектами.
Указатель на функцию является объектом функции, но у них также есть забавная причуда, пришедшая из эпохи C:
#include <iostream>
int foo(int a)
{
std::cout << "Hello, " << a << " stars\n";
return a;
}
int (*pr) (int) = foo;
int main()
{
pr(0);
(*pr)(1);
(**pr)(2);
(***pr)(3);
return 0;
}
Постфиксное выражение должно иметь тип функции или указатель на тип функции, а функция может быть контекстно преобразована в указатель функции всякий раз, когда такое преобразование требуется, поэтому повторяющиеся операторы разыменования возвращают тип «туда-сюда», оставляя его указателем на функцию. Лямбда без захвата тоже подойдет:
// also can write `auto pr2 = ...`
int (*pr2) (int) = [](int a)->int {
std::cout << "Hello, lambda " << a << " stars\n"; return a;
};
// in main()
(**pr2)(2);
Естественно, этот код плохо сформирован, если pr - обобщенный "функтор"
int b = 2;
auto pr3 = [=]()->int {
std::cout << "Hello, general lambda " << b << std::endl;
return b;
};
pr3();
//(*pr3)(); Ill-formed: not a pointer!