c++ 为什么无状态函子的operator()不能是静态的?

ghg1uchk  于 2023-02-06  发布在  其他
关注(0)|答案(8)|浏览(132)

为什么无状态函子的operator ()不允许是static?无状态lambda对象可以转换为指向自由函数的指针,这些自由函数与它们的operator ()具有相同的签名。
第6页的Stephan T. Lavavej指出,转换为函数指针 * 只是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 ()没有过载。

bvhaajcl

bvhaajcl1#

根据标准13.5/6,

运算符函数必须是非静态成员函数或非成员函数,并且至少有一个类型为类、类引用、枚举或枚举引用的参数。

此外,在13.5.4中规定
操作员()应该是一个非静态成员函数,具有任意数量的参数。它可以有默认参数。它实现了函数调用语法postfix-expression(expression-list opt),其中后缀表达式的计算结果为类对象,并且可能为空的expression-list与运算符的参数列表匹配因此,对于类型T的类对象x,调用x(arg 1,...)被解释为x.operator()(arg 1,...)

7dl7o3gd

7dl7o3gd2#

我认为没有技术上的理由禁止这样做(但是由于不熟悉事实上的跨供应商C++ ABI(安腾ABI),我不能保证任何事情)。
然而,在www.example.com上有一个关于这个的进化问题https://cplusplus.github.io/EWG/ewg-active.html#88。它甚至有 [tiny] 标记,使它成为一个考虑中的有点“琐碎”的特性。

8wigbo56

8wigbo563#

我看不出有什么技术上的理由来禁止static auto operator()( ... ),但它是一种特殊情况,所以增加对它的支持会使标准复杂化,而这种复杂化是不必要的,因为它很容易模拟:

struct L
{
    static void func() {}

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

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

有关一些可能有用的额外信息,请参见Johannes' answer

yxyvkwin

yxyvkwin4#

像其他人一样,我看不出为什么对于无状态函子或 * 一般情况下 * 不可能有静态operator()的根本原因。
(EDIT 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的用户不需要知道函数是作为静态函数还是成员函数实现的,它可以在以后从泛型的Angular 升级为成员函数。
撇开泛型编程这个重要的应用程序不谈,有一个变通方法会导致类似的 * 语法 *,我在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()这样的东西会非常有趣,但它甚至不遵循静态函数的语法,可能会与构造函数混淆)。
(As我说,关于您指出的这个限制,我仍然怀念的唯一功能是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);
}
eivnm1vs

eivnm1vs5#

从C++23开始,operator()P1169)和operator[]P2589)都可以是static,这个限制从来没有真正的好理由,所以它终于被取消了。
您也可以将Lambda定义为static[] static { ... }。如果您有一个没有捕获的Lambda,则应始终将其定义为static
不幸的是,这种行为不能成为隐式标准而不破坏API和ABI兼容性,因此显式定义是必要的。向后兼容性通常既是福也是祸。

cbeh67ev

cbeh67ev6#

我我根本不打算假装是一个Maven,但是,我正在研究静态lamdas或静态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

w8ntj3qf

w8ntj3qf7#

现在可以在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 trunk的编译器资源管理器示例。

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] ));
}
vdgimpew

vdgimpew8#

一个简单的,有点肮脏的变通方案,直到有关 * 委员会 * 考虑这个微不足道的功能:

  • Glob运算符在语法上类似于构造函数。*

因此,您不能编写

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

这是不可能的,因为一个"委员会"在不明确的原因下做出了这样的决定。如果我能和他们中的一个成员谈谈,问问他们做出这样的决定时是怎么想的,我会很高兴。不幸的是,他可能不会理解我的问题,因为他从来没有编写过c++程序。他只做过它的文档。
现在他们需要几十年的

  • 辩论
  • 协商
  • 会议
  • 和考虑因素

来实现一个微不足道的特性。我怀疑这个特性可能在c++3x中可用,甚至可能在仿真机器上试用。
在此之前,您可以尝试编写:

MyClass::MyClass(...);

在这两种情况下,都可以调用MyClass(...);
当然,如果MyClass是单例的话,它是很有用的。而且,我会说它是一个黑客。此外,它在堆栈上分配了一个sizeof(MyClass),这可能在性能/效率方面很糟糕。
此外,这本质上是一个构造函数,构造函数不能返回任何值,但是你可以通过将结果存储在示例中,然后用一个强制转换操作符将其强制转换为你想要的任何值来避免这种情况。

相关问题