11

stackoverflow で質問を読んで、自分自身へのポインターを取る関数を宣言できるかどうか疑問に思いました。つまり、次のように を宣言するfooと、次のようになります。

foo(foo);

最も単純なアイデアは、別の関数ポインターにキャストすることです (void*小さい可能性があるため、 にキャストすることはできません)。したがって、関数宣言は次のようになります。

void foo(void (*)());

それは C では問題ありませんが (C++ でのキャストでも機能します)、そのようなハードな「再解釈」キャストや型情報の損失なしに実行できるかどうか疑問に思います。

つまり、次の宣言が必要です。

void foo( void (*)( void (*)( void (*) ( ... ))));

しかしもちろん、無制限の宣言は不可能です。ナイーブtypedefも役に立ちません。

foo(foo)C++ テンプレートは、呼び出しコード ( ) が少し簡潔に見えなくても歓迎されますが、それでも有限です。

キャストせずに型情報を削除する方法、またはそのような他のトリックを示すCスタイルの回答は、もちろん興味深いですが、受け入れられません。

4

8 に答える 8

5

どうやらそうではありません -このスレッドを参照してください。ここで必要な型は常に無限です。

于 2009-10-03T19:35:38.660 に答える
5

別の汚いトリック。

void Foo( ... )
{
}

int main()
{
 Foo( Foo );
}

上記のプログラムは、エラーなしでコンパイルされます。しかし、再帰的ではありません。次の変更された関数は、リミッター付きの再帰バージョンです。

#define RECURSIVE_DEPTH (5)

typedef void ( *FooType )( int, ... );

void Foo( int Depth, ... )
{
 void ( *This )( int, ... );

 va_list Arguments;

 va_start( Arguments, Depth );

 if( Depth )
 {
  This = va_arg( Arguments, FooType );

  This( Depth - 1, This );
 }

 va_end ( Arguments );  
}

int main()
{
 Foo( RECURSIVE_DEPTH, Foo );
}
于 2009-10-07T08:20:50.450 に答える
4

はい

これは、 「自分自身へのポインターを返す関数を作成できますか?」の変形です。ただし、あなたの場合、関数型は戻り値の型としてではなく、引数ケントとして再帰的に表示されます。ただし、Herb Sutters の回答は再利用可能です。ポインターを前方宣言されたプロキシ クラスにラップします。

于 2009-10-05T10:26:43.410 に答える
3

一般的に、私は Dario に同意します。これを型レベルで作成することは不可能に思えます。

ただし、クラス (「戦略パターン」) を使用できます。

class A {
  void evil(A a) {       // a pointer to A is ok too
  }
};

operator() を追加することもできます:

  void operator()(A a) { return evil(a); }

一般に、そのようなことは FP 言語で行う方が適切です。Haskell のバージョンは次のとおりです。

data Evil = Evil (Evil -> Integer)

Evilこれは、クラスの使用に対応するラッパー ( ) を使用します。

質問者から: この回答に欠けていたのは、いくつかの異なる関数をそれらの 1 つの引数として渡す機能でした。evil()これは、仮想化するか、それを実装する関数ポインタをオブジェクトに明示的に格納することで解決できます(基本的には同じです)。

この明確化により、答えは受け入れられるのに十分です。

于 2009-10-03T19:44:39.870 に答える
2

関連する問題は、同じ型の関数ポインタを返すことです。ステート マシンを実装するときに表示されるため、C FAQ に独自のエントリがありました。

同じ回避策を問題に適用できます。

于 2009-10-03T20:05:50.270 に答える
1

関数をクラスに入れたり、関数にそのクラスを引数として使用させたりするなど、なんらかのトリックなしに、C++で引数として自分自身を取得できる関数を作成できるとは思いません。

新しいC++標準がヒットしたときに可能になる別の方法は、ラムダを使用し、ラムダ自体をキャプチャすることです。私はそれが次のようになると思います:

auto recursive_lambda = [&recursive_lambda] { recursive_lambda(); };

このようなステートメントは、ラムダをサポートするコンパイラではまったくテストされていないことに注意してください。あなたのマイレージは異なる場合があります。

于 2009-10-03T20:22:20.353 に答える
0

関数がそれ自体を引数として取ることができる場合、それはそれ自体を呼び出すことによって無名再帰を実行することができます。しかし、単純型付きラムダ計算では再帰は不可能です(これは基本的に、関数型でここにあるものです)。匿名再帰を実行するには、再帰関数または再帰型を使用して固定小数点コンビネーターを実装する必要があります。

于 2009-10-03T19:49:12.683 に答える
0

これは の完全に有効な使用方法ですvoid *

typedef void T(void *);

void f(T *probably_me)
{
  (*probably_me)(f);
}
于 2009-10-03T19:43:39.767 に答える