为什么无状态函子的运算符 () 不能是静态的?

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

本文关键字:不能 静态 运算符 状态 为什么      更新时间:2023-10-16

为什么不允许static无状态函子operator ()?无状态 lambda 对象可转换为指向与其operator ()具有相同签名的自由函数的指针。

Stephan T. Lavavej在第6页指出,转换为函数指针只是一个operator FunctionPointer() (引用(。但是我无法获得指向非成员函数的operator ()的相应指针。对于函子struct F { void operator () () {} }似乎不可能将&F::operator ()转换为类型 using P = void (*)(); 的实例。

法典:

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

错误是

重载的"operator(("不能是静态成员函数

operator ()并没有超载。

根据标准 [over.oper] p6:

运算符函数应为非静态成员函数或非成员函数,并且具有至少一个参数,其类型为类、对类的引用、枚举或对列举。

此外,在 [over.call] p1 中指出:

operator()应是具有任意数量的参数的非静态成员函数。它可以有默认参数。它实现了函数调用语法

后缀表达式(表达式列表选择)

其中后缀表达式计算为类对象和可能为空的表达式列表比赛operator()的参数列表类的成员函数。因此,一个电话 x(arg1,...)被解释如 x.operator()(arg1, ...)对于类对象 Tx .

<小时 />

注意:这些限制已在 C++23 中解除,因此可以static operator()

我看不出有任何技术理由禁止static auto operator()( ... ).但这是一个特例,因此添加对它的支持会使标准复杂化。这种复杂性是不必要的,因为它很容易模仿:

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

请参阅约翰内斯的回答,以获取一些可能有用的额外信息。

像其他人一样,我没有看到为什么不可能有一个静态operator()的根本原因,对于无状态函子或一般而言

(编辑2020:刚刚找到这个提案 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1169r0.html(

(2021 年更新:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1169r1.html(

在某些情况下,根据其他规则,可能会与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 _() {} 
    auto operator()() const{ 
        return L::_();
    }
};
L ell; 
ell(); // calls L::_() really.

不知道还愿意实现什么。

<小时 />

人们可以"CRTP"这个想法 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);
}
我认为

没有技术理由禁止这样做(但不熟悉事实上的跨供应商C++ ABI(Itanium ABI(,我不能保证任何事情(。

然而,在 https://cplusplus.github.io/EWG/ewg-active.html#88 有一个进化问题。它甚至有[微小]标记,使其成为正在考虑的"微不足道"的功能。

从 C++23 开始,operator() (P1169( 和operator[] (P2589( 都可以static。从来没有一个很好的理由来限制,所以它终于被取消了。

您还可以将 Lambda 定义为 [] static { ... }static。如果您有没有捕获的 Lambda,则应始终将其定义为 static .

遗憾的是,如果不破坏 API 和 ABI 兼容性,此行为就无法成为隐式标准,因此显式定义是必要的。向后兼容性通常既是祝福也是诅咒。

我根本不

会假装自己是专家。 但是,我正在研究静态lamda或static operator((以及他们正在进行的提案。 我对我能理解的东西很满意。

看来提出的想法正在努力解决如何设计它以不破坏其他代码等,但是他们似乎正在研究解决方案。 所以,有一天马比耶!

static operator() 
Document #: P1169R2  
Date:   2021-08-14
Project: Programming Language C++

3 提案

该建议是只允许使呼叫操作员成为静态成员函数,而不是要求它是非静态的成员函数。我们在无会员方面拥有多年的经验函数对象很有用。让我们删除不必要的对象参数开销。似乎没有提供任何价值此限制。

目前还有其他运营商需要实现为非静态成员函数 - 所有一元运算符,赋值、下标、转换函数和类成员访问。我们认为,不能将其中任何一项声明为静态将具有同样多的价值,所以我们不追求这些价值。时间。我们不知道有任何用例可以制作任何其他运算符是静态的,而具有无状态功能的用例对象非常常见。

在此处查找完整文档:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1169r2.html

现在可以在 c++23 之后使用静态运算符 (( - 转到 https://en.cppreference.com/w/cpp/feature_test#Language_features 并查找__cpp_static_call_operator 。GCC 13 将支持它 - https://gcc.gnu.org/projects/cxx-status.html。

下面是一个带有 gcc 主干的编译器资源管理器的示例。

struct A {
    constexpr static int operator() (int x) noexcept { return x - 1; }
    constexpr static int operator[] (int x) noexcept { return x - 1; }
};
int main() {
    return [](int x) constexpr static noexcept { return x - 1; }( A()( A()[3] ));
}
<</div> div class="answers">

在相关委员会考虑这个微不足道的功能之前,一个简单、有点肮脏的解决方法:

glob 运算符在语法上类似于构造函数。

因此,你不能写一个

static MyClass::operator()(...);

这根本不可能,因为一个委员会根据不清楚的原因做出了这样的决定。如果我能和他们的一位成员交谈,问问他们在决定这样做时的想法,我会很高兴。不幸的是,他可能不会理解我的问题,因为他从未编写过c ++。他只处理其文档。

现在他们需要几十年的

  • 辩论
  • 磋商
  • 会议
  • 和注意事项

实现一个微不足道的功能。我怀疑该功能可能在 c++3x 中可用,在模拟机器上甚至可以试用。

在此之前,您可以尝试编写:

MyClass::MyClass(...);

在这两种情况下,您都可以调用MyClass(...);

当然,它主要是在MyClass是单例的情况下有用。而且,我会说这是一个黑客。此外,它在堆栈上分配了一个sizeof(MyClass),这在性能/效率方面可能很糟糕。


此外,这本质上是一个构造函数,构造函数不能返回任何内容。但是,您可以通过将结果存储在实例中,然后由强制转换运算符将其强制转换为所需的任何内容来避免这种情况。