Почему оператор() функтора stateless не статичен?
Почему operator()
функтора без сохранения состояния не может быть static
? Лямбда-объекты без сохранения состояния можно преобразовать в указатели на свободные функции, имеющие ту же сигнатуру, что и их operator()
.
Стефан Т. Лававей на р. 6 указывает, что преобразование в указатель на функцию - это просто operator FunctionPointer()
(cite). Но я не могу получить соответствующий указатель на operator()
относительно функции, не являющейся членом. Для функторной struct F { void operator()() {} }
кажется невозможным преобразовать &F::operator()
в экземпляр типа, using P = void (*)();
,
Код:
struct L
{
static
void operator () () const {}
operator auto () const
{
return &L::operator ();
}
};
Ошибка
перегруженный оператор() не может быть статической функцией-членом
но operator()
не перегружен.
Ответы
Ответ 1
В стандарте 13.5/6,
Операторная функция должна быть либо нестатической функцией-членом, либо быть не-членной функцией и иметь по крайней мере один параметр, тип которого является классом, ссылка на класс, перечисление или ссылка на перечисление.
Кроме того, в 13.5.4 говорится, что
оператор() должна быть нестатической функцией-членом с произвольным числом параметров. Он может иметь аргументы по умолчанию. Он реализует синтаксис вызова функции постфикс-выражение ( список_выражений выбирать ) где постфикс-выражение оценивает объект класса и возможно пустую список_выражений Матчи список параметров Оператор() член функции класса. Таким образом, вызов х (arg1,...) интерпретируется в виде x.operator() (arg1,...) для объекта класса Икс типа Т
Ответ 2
Я думаю, что нет никаких технических причин, чтобы запретить это (но, будучи не знакомым с де-факто кросс-вендором C++ ABI (Itanium ABI), я ничего не могу обещать).
Однако, существует эволюционная проблема по этому поводу на https://cplusplus.github.io/EWG/ewg-active.html#88. На нем даже есть [крошечная] отметка, что делает ее несколько "тривиальной" рассматриваемой особенностью.
Ответ 3
Я не вижу никаких технических причин, чтобы запретить static auto operator()( ... )
. Но это особый случай, так что это усложнит стандарт, чтобы добавить поддержку для него. И такое усложнение не требуется, потому что очень легко подражать:
struct L
{
static void func() {}
void operator()() const { func(); }
operator auto () const
{
return &L::func;
}
};
См. ответ Йоханнеса для некоторой, возможно, полезной дополнительной информации.
Ответ 4
Простой, немного грязный обходной путь, пока соответствующий комитет не рассмотрит эту тривиальную функцию:
Операторы glob синтаксически похожи на конструкторы.
Таким образом, вы не можете написать
static MyClass::operator()(...);
Это было сделано просто невозможно, потому что комитет принял решение по непонятным причинам. Я был бы так счастлив, если бы мог поговорить с одним из их членов, спросить, что было у них в голове, когда они так решили. К сожалению, он, вероятно, не поймет мой вопрос, потому что он никогда не программировал С++. Он работал только над своими документами.
Теперь им нужно несколько десятилетий
- дебаты
- консультации
- встречи
- и соображения
для реализации тривиальной функции. Я подозреваю, что эта функция может быть доступна в С++ 3x, а на эмулированных машинах она может быть даже опробована.
До тех пор вы можете попытаться написать:
MyClass::MyClass(...);
В обоих случаях вы можете вызвать MyClass(...);
.
Конечно, это полезно, главным образом, если MyClass
является одноэлементным. И, я бы сказал, это взломать. Кроме того, он выделяет sizeof(MyClass)
в стеке, который может быть плохим с точки зрения производительности/эффективности.
Кроме того, это будет по существу конструктором, а конструкторы ничего не могут вернуть. Но вы можете избежать этого, сохранив результат в экземпляре, а затем отбросив его оператором трансляции на все, что хотите.
Ответ 5
Как и другие, я не вижу фундаментальной причины, почему это невозможно.
В некоторых случаях может конфликтовать, согласно другим правилам, с перегрузкой элементов/статических элементов, которая не разрешена в C++ (опять же, не знаю, почему).
struct A{
void f();
static int f(); // compile error
}
Таким образом, даже если бы было разрешено иметь static operator()
, это должно быть разрешено?
struct A{
void operator()();
static int operator()(); // should be a compiler error???
}
В любом случае, есть только одна истинная причина иметь static operator()
который не является чисто синтаксической причиной, и что объекты должны иметь возможность вызывать статические функции, как если бы они были функциями-членами.
struct A{
static int f():
}
...
A a;
a.f(); // calls A::f() !!!
В частности, пользователю класса A
не нужно знать, реализована ли функция как статическая или как член. Позднее его можно обновить до функции-члена с общей точки зрения.
Если оставить в стороне это важное приложение для общего программирования, то есть обходной путь, ведущий к аналогичному синтаксису, который я видел в https://quuxplusone.github.io/blog/2018/03/19/customization-points-for-functions/, и это должно иметь статическую функцию-член с именем _
, имя, которое не подразумевает никакого значения.
struct A{
static int _();
}
...
A::_(); // instead of the more desirable (?) A::() or A::operator()
a._(); // this invokes the static functon _
Вместо более желательного A::()
или A::operator()
, (ну, они вообще желательны? Я не знаю; что-то вроде A()
было бы действительно интересно, но даже не следовало синтаксису статическая функция и может быть перепутана с конструктором).
(Как я уже сказал, единственная особенность, которую я все еще упускаю из-за этого ограничения, на которое вы указали, - это то, что a()
не может автоматически делегироваться статической версии operator()
, например, A::operator()
.)
В итоге ваш код может выглядеть так:
struct L{
static void _() const {}
operator auto () const{
return &L::_();
}
};
Не уверен, что готовы достичь еще.