На странице «Вызов» в cppreference указано:
Примечания. Указатели на элементы данных являются вызываемыми, даже если вызовы функций не выполняются.
Указатели на члены функций имеют смысл: почему указатели на члены данных являются вызываемыми, если их нельзя вызвать?
Я бы сказал, что «вызываемый» может быть более подходящим именем, потому что оно напрямую относится к INVOKE, но в любом случае «вызываемый» — это то, что сейчас есть в стандарте.





Стандарт определяет это так. [func.def]/3
Вызываемый тип — это тип функционального объекта ([function.objects]) или указатель на член.
Стандарты определяют INVOKE для указателей на члены данных.
func.require
Определите
INVOKE(f, t1 , t2 , …, tN )следующим образом:(1.4) -
t1.*fкогдаN = 1иfявляются указателями на член данных классаTиis_same_v<T, remove_cvref_t<decltype(t1)>> || is_base_of_v<T, remove_cvref_t<decltype(t1 )>>истинно;(1.5) -
t1.get().*fкогдаN = 1иfявляются указателями на член данных классаTиremove_cvref_t<decltype(t1 )>является специализацией reference_wrapper;(1.6) -
(*t1 ).*fкогдаN = 1иfявляются указателями на член данных классаTиt1не удовлетворяет двум предыдущим пунктам;
Грубо говоря, результат вызова элемента данных — это значение элемента данных.
Указатели на члены данных можно «вызывать» в том смысле, что их можно вызывать, даже если фактического вызова функции не происходит. См. Функциональные объекты ‒ указатель на элемент данных можно «вызвать», извлекая член целевого объекта, что также делает std::invoke, когда ему дан указатель на член.
Почему, я предполагаю удобство. Полезно иметь возможность обрабатывать указатель на элемент как функцию, которая просто извлекает элемент, поскольку с такой вещью, как правило, больше ничего нельзя сделать, как и с указателями на функции.
Указатели на члены данных являются вызываемыми типами по определению.
Обратите внимание, что это не относится к использованию оператора вызова (), поскольку к указателям на члены данных и указателям на функции-члены не может быть применен оператор (); однако их можно вызвать.
Выражение только для экспозиции INVOKE (доступное через std::invoke) работает с любым вызываемым типом.
Для указателя элемента данных fINVOKE(f, t) расширяется до t.*f ([func.require] p1.4).
Таким образом, указатели на члены данных действуют как функция, возвращающая член объекта.
Они могут быть очень полезны в качестве проекций в новых алгоритмах C++20:
struct S { int x; };
std::vector<S> range = /* ... */;
// sort range using std::ranges::less and using &S::x as a projection
std::ranges::sort(range, {}, &S::x);
Такой вызов std::ranges::sort будет сортировать range путем сравнения члена x каждого объекта S.
Это работает, поскольку алгоритмы принимают любой вызываемый тип, а не только типы с оператором вызова.
Согласитесь, терминология очень запутанная. Оглядываясь назад, вместо этого «вызываемые типы» следовало бы назвать «вызываемыми типами».
Также были предложения добавить оператор () к указателям на члены; совсем недавно появился P1214: указатель на функции-члены и объекты-члены — это просто вызываемые объекты!.
Это похоже на Python property, где доступ к нему через экземпляр действительно вызывает метод property__get__ с экземпляром в качестве аргумента.
@chepner Note that this doesn't refer to the use of the call operator () это определение из библиотечного API, а не из синтаксиса языка (где указатели на члены представляют собой отдельный класс типов, отличный от указателей на функции или других указателей). По сути, это говорит о том, что функции, которые могут принимать вызываемый объект, должны иметь перегрузку для указателя на член.
В C++ термин «вызываемый» может иметь множество значений, особенно когда речь идет об указателях на элементы данных. Хотя номенклатура может поначалу показаться нелогичной, она помогает понять контекст и более широкое определение термина «вызываемый» в контексте C++. В определенном смысле указатели на элементы данных считаются вызываемыми. В основном это связано с тем, как к ним можно получить доступ, изменить их и взаимодействовать с использованием синтаксиса. По этой причине они считаются вызываемыми
Вот пример
#include <iostream>
using namespace std;
class MyClass {
public:
int x;
float y;
};
int main() {
MyClass obj;
obj.x = 10;
obj.y = 3.14f;
// Pointer to data member
int MyClass::*pX = &MyClass::x;
// Applying the pointer to the data member
cout << "Value of x: " << obj.*pX << endl; // Output: Value of x: 10
return 0;
}
В этом коде obj.*pX выглядит и ведет себя как вызов функции, но на самом деле он обращается к элементу x объекта obj через указатель.
Указатели на участников очень полезны в качестве функций проекции. Предположим, у вас есть
struct S { int i; int j; }и вы хотите найти элемент вstd::vector<S> vecпоi = 1. Вы можете сделать:std::ranges::find(vec, 1, &S::i);. Никаких лямд.