web-dev-qa-db-ja.com

ステートレスファンクターの演算子()を静的にできないのはなぜですか?

ステートレスファンクターのoperator ()staticにできないのはなぜですか?ステートレスラムダオブジェクトは、operator ()と同じ署名を持つフリー関数へのポインターに変換できます。

Stephan T. Lavavej p。 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 ()はオーバーロードされていません。

17

標準13.5/6に従って、

演算子関数は、非静的メンバー関数または非メンバー関数のいずれかでなければなりませんそして、タイプがクラス、クラスへの参照、列挙型、または参照であるパラメーターを少なくとも1つ持っている列挙型に。

さらに、13.5.4では次のように述べられています

operator()は、任意の数のパラメーターを持つ非静的メンバー関数でなければなりません。デフォルトの引数を持つことができます。これは、postfix-expressionがクラスオブジェクトに評価され、空の可能性があるexpression-listがクラスのoperator()メンバー関数のパラメーターリストと一致する関数呼び出し構文postfix-expression(expression-list opt)を実装します。したがって、呼び出しx(arg1、...)は、タイプTのクラスオブジェクトxのx.operator()(arg1、...)として解釈されます。

12
SingerOfTheFall

これを禁止する技術的な理由はないと思います(ただし、事実上のクロスベンダーC++ ABI(Itanium ABI)に精通していないため、何も約束できません)。

ただし、これについては https://cplusplus.github.io/EWG/ewg-active.html#88 で進化的な問題があります。 [tiny]マークも付いているので、検討中のやや「些細な」機能になっています。

static auto operator()( ... )を禁止する技術的な理由がわかりません。ただし、これは特殊なケースであるため、サポートを追加するための標準が複雑になります。そして、エミュレートするのは非常に簡単なので、そのような複雑さは必要ありません。

struct L
{
    static void func() {}

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

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

役立つ可能性のある追加情報については、 ヨハネスの回答 を参照してください。

他の人と同じように、それが不可能な根本的な理由はわかりません。

場合によっては、他のルールに従って、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::_();
    }
};

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

まだ何を達成しようとしているのかわからない。

2
alfC

関連する委員会がこの些細な機能を検討するまでの単純で少し汚い回避策:

Glob演算子は構文的にコンストラクターに似ています。

したがって、あなたは書くことはできません

_static MyClass::operator()(...);
_

委員会が不明確な理由でそう決定したので、それは単に不可能にされました。メンバーの一人と話をして、彼らがそう決めたときに何を考えていたのか聞いていただければ幸いです。残念ながら、彼はc ++をプログラミングしたことがないため、おそらく私の質問を理解できないでしょう。彼はそのドキュメントにのみ取り組んだ。

今、彼らは数十年を必要とします

  • 討論
  • 相談
  • 会議
  • および考慮事項

些細な機能を実装します。この機能はc ++ 3xで利用できるようになる可能性があり、エミュレートされたマシンでは試してみることができるかもしれません。

それまでは、次のように書くことができます。

_MyClass::MyClass(...);
_

どちらの場合も、MyClass(...);を呼び出すことができます。

もちろん、主にMyClassがシングルトンの場合に役立ちます。そして、それはハックだと思います。さらに、スタックにsizeof(MyClass)を割り当てますが、これはパフォーマンス/効率の面で悪い可能性があります。


さらに、これは本質的にコンストラクターであり、コンストラクターは何も返すことができません。ただし、結果をインスタンスに保存し、キャスト演算子によって任意の場所にキャストすることで、これを回避できます。