Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't operator () of stateless functor be static?

Why is operator () of stateless functor not allowed to be static? Stateless lambda objects are convertible to pointers to free functions having the same signature as their operator ().

Stephan T. Lavavej on p. 6 points out that conversion to a function pointer is just an operator FunctionPointer() (cite). But I can't obtain a corresponding pointer to operator () as to non-member function. For functor struct F { void operator () () {} } it seems to be impossible to convert &F::operator () to instance of type using P = void (*)();.

Code:

struct L
{
    static
    void operator () () const {} 
    operator auto () const
    { 
        return &L::operator ();
    }
};

The error is

overloaded 'operator()' cannot be a static member function

but operator () is not overloaded.

like image 359
Tomilov Anatoliy Avatar asked Sep 25 '15 06:09

Tomilov Anatoliy


4 Answers

Per standard 13.5/6,

An operator function shall either be a non-static member function or be a non-member function and have at least one parameter whose type is a class, a reference to a class, an enumeration, or a reference to an enumeration.

Additionally, in 13.5.4 it is stated that

operator() shall be a non-static member function with an arbitrary number of parameters. It can have default arguments. It implements the function call syntax postfix-expression ( expression-list opt ) where the postfix-expression evaluates to a class object and the possibly empty expression-list matches the parameter list of an operator() member function of the class. Thus, a call x(arg1,...) is interpreted as x.operator()(arg1, ...) for a class object x of type T

like image 116
SingerOfTheFall Avatar answered Sep 23 '22 16:09

SingerOfTheFall


I would think that there's no technical reason to forbid this (but not being familiar with the de-facto cross-vendor C++ ABI (Itanium ABI), I can't promise anything).

There's however an evolutional issue about this at https://cplusplus.github.io/EWG/ewg-active.html#88 . It even has the [tiny] mark on it, making it a somewhat "trivial" feature under consideration.

like image 29
Johannes Schaub - litb Avatar answered Sep 25 '22 16:09

Johannes Schaub - litb


I can't see any technical reason to forbid a static auto operator()( ... ). But it's a special case, so it would complicate the standard to add support for it. And such complication is not necessary, because it's very easy to emulate:

struct L
{
    static void func() {}

    void operator()() const { func(); }

    operator auto () const
    { 
        return &L::func;
    }
};

See Johannes' answer for some possibly useful extra info.

like image 36
Cheers and hth. - Alf Avatar answered Sep 24 '22 16:09

Cheers and hth. - Alf


Like the others, I don't see a fundamental reason why it is not possible to have a static operator(), for stateless functors or in general.

(EDIT 2020: Just found this proposal http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1169r0.html)

(UPDATE 2021: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1169r1.html)

In some cases might conflict, according to other rules, with member/static overloading which is not allowed in C++ (again, not sure why).

struct A{
  void f();
  static int f(); // compile error 
}

So even if it were allowed to have a static operator(), should this be permitted?

struct A{
  void operator()();
  static int operator()(); // should be a compiler error??? 
}

Anyway, there is only one true reason to have a static operator() that it is not purely a syntactic reason and it is that objects should be able to call static functions as if they were member functions.

struct A{
   static int f():
}
...
A a; 
a.f(); // calls A::f() !!!

Specifically, the user of the class A doesn't need to know if a function is implemented as static or as a member. It can later be upgraded to a member function from a generic point of view.

Leaving that important application to generic programming aside, there is a workaround the leads to a similar syntax that I saw in https://quuxplusone.github.io/blog/2018/03/19/customization-points-for-functions/, and that is to have a static member function called _, a name that doesn't imply any meaning.

struct A{
    static int _();
}
...
A::_(); // instead of the more desirable (?) A::() or A::operator()
a._(); // this invokes the static functon _ 

Instead of the more desirable A::() or A::operator(), (well are they desirable at all? I don't know; something like A() would be really interesting but doens't even follow the syntax of a static function and can be confused with a constructor).

(As I said, the only feature I still miss, regarding this limitation you point out, is that a() cannot automatically delegate to a static version of the operator(), e.g. A::operator().)

In summary, your code could look like this:

struct L{
    static void _() {} 
    auto operator()() const{ 
        return L::_();
    }
};

L ell; 
ell(); // calls L::_() really.

Not sure what are willing to achieve still.


One can "CRTP" the idea https://godbolt.org/z/74vxsTYxd

#include<utility> // forward

template<class Self>
struct stateless_functor_facade{
    template<class... Args>
    constexpr decltype(auto) operator()(Args&&... args) const{
        return Self::operator_paren(std::forward<Args>(args)...);
    }
};

struct square : stateless_functor_facade<square>{
    static auto operator_paren(double x){return x*x;}
};

int main(){
    square s;
    s(5);
}
like image 33
alfC Avatar answered Sep 25 '22 16:09

alfC