Являются ли указатели функций функциональными объектами в 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!
Другие вопросы по тегам