4

次のコードをコンパイル可能にする型 T はどれですか?

T f(){ return &f; }

Cの回答を希望しますが、テンプレートを使用した回答しかない場合に備えて、質問をCおよびC++とマークしました。

4

8 に答える 8

9

興味深い問題ですが、許容できる C ソリューションがないと思います。

なぜこれが C で不可能なのですか? (ここからは推測です)

T を返す関数の型は次のとおりです。

T (*)(void) ;

もちろん、Tが定義されていることを期待しています...しかし、Tは関数自体の型であるため、循環依存があります。

構造体 T の場合、次のようにすることができます。

struct T ;               /* forward declaration */
typedef T * (*f)(void) ; /* f is a function returning a pointer to T */

次の表記は便利ではないでしょうか?

function T ; /* fictional function forward-declaration.
                It won't compile, of course */
T T(void) ;  /* function declaration */

ただし、関数を前方宣言する方法がないため、質問で記述した構成を使用する方法はありません。

私はコンパイラの専門家ではありませんが、この循環依存関係は、C/C++ の制限ではなく、typedef 表記が原因でのみ作成されると考えています。結局のところ、関数ポインター (ここでは関数について話しているのであって、オブジェクト メソッドではありません) はすべて同じサイズです (構造体またはクラス ポインターがすべて同じサイズであるのと同じように)。

C++ ソリューションの学習

C++ ソリューションに関しては、以前の回答で良い回答が得られました (ここでzildjohn01 の回答について考えています)。

興味深い点は、それらはすべて、構造体とクラスを前方宣言できるという事実に基づいていることです (そして、それらの宣言本体で前方宣言されていると見なされます)。

#include <iostream>

class MyFunctor
{
   typedef MyFunctor (*myFunctionPointer)() ;
   myFunctionPointer m_f ;
   public :
      MyFunctor(myFunctionPointer p_f) : m_f(p_f) {}
      MyFunctor operator () ()
      {
         m_f() ;
         return *this ;
      }
} ;

MyFunctor foo()      {
   std::cout << "foo() was called !" << std::endl ;
   return &foo ;
}

MyFunctor barbar()   {
   std::cout << "barbar() was called !" << std::endl ;
   return &barbar ;
}

int main(int argc, char* argv[])
{
   foo()() ;
   barbar()()()()() ;
   return 0 ;
}

どの出力:

foo() was called !
foo() was called !
barbar() was called !
barbar() was called !
barbar() was called !
barbar() was called !
barbar() was called !

C++ ソリューションから C ソリューションに到達するためのインスピレーション

同等の結果を得るために、C で同様の方法を使用できないでしょうか?

どういうわけか、そうですが、結果は C++ ソリューションほど魅力的ではありません。

#include <stdio.h>

struct MyFuncWrapper ;

typedef struct MyFuncWrapper (*myFuncPtr) () ;

struct MyFuncWrapper { myFuncPtr f ; } ;

struct MyFuncWrapper foo()
{
   printf("foo() was called!\n") ;

   /* Wrapping the function */
   struct MyFuncWrapper w = { &foo } ; return w ;
}

struct MyFuncWrapper barbar()
{
   printf("barbar() was called!\n") ;

   /* Wrapping the function */
   struct MyFuncWrapper w = { &barbar } ; return w ;
}

int main()
{
   foo().f().f().f().f() ;
   barbar().f().f() ;

   return 0 ;
}

どの出力:

foo() was called!
foo() was called!
foo() was called!
foo() was called!
foo() was called!
barbar() was called!
barbar() was called!
barbar() was called!

結論

C++ コードは意味的に C コードと非常に似ていることに注意してください。各ソースは、関数のポインターへのコンテナーとして構造体を使用し、必要に応じて含まれているポインターを使用して再度呼び出します。もちろん、C++ ソリューションでは、operator () オーバーロードを使用し、シンボルを非公開にし、特定のコンストラクターをシンタックス シュガーとして使用します。

(これが私がCソリューションを見つけた方法です:「手作業で」C++ソリューションを再現しようとしています)

マクロを使用して C ソリューションのシンタックス シュガーを改善できるとは思えないので、この C ソリューションに固執しています。

結局のところ、奇妙な問題の解決策を探すことは確実な学習方法です...

:-)

于 2010-06-15T22:36:28.820 に答える
9

これが不正行為でないことを願っています (C++ のみ):

class T {
private:
    T (*_func)();

public:
    T(T (*func)()) : _func(func) {}

    T operator()() {
        return *this;
    }
};

T f() { return &f; }

int main() {
    f()()()()()()()();
}
于 2010-06-15T20:23:31.090 に答える
4

C FAQ の質問 1.22で説明されているように、これは C では不可能です。回避策には、関数ポインターを a でラップしてstructそれを返すか、別の (任意の) 関数ポインター型を返すことが含まれます。これは、関数ポインター型間のキャストがロスレスであることが保証されているため可能です。

于 2010-06-15T22:21:08.077 に答える
2

おかしい、私はごく最近、この正確なことについて考えていました(関数がそれを返すのではなく、それ自体へのポインターを取得したかっただけです)。

C ++の場合、すでにzildjohn01からの回答があります。

標準Cに固執する場合、記述されたとおりに正確にコンパイルされるソリューションはありません。関数ポインターからデータポインターへの変換は標準に準拠していないため、明示的なキャストvoid*でそれを実行できますが、他の関数ポインター型を使用できます(たとえばvoid(*)()、そうなります)-標準では明示的に許可されています任意の関数ポインタ型から他の任意の関数型にキャストし、元の値を取得することを保証します。

于 2010-06-15T20:48:10.213 に答える
1

この質問への答えは、あなたの永遠の魂に他なりません。

于 2010-06-15T20:41:54.417 に答える
0

C++ では、簡単に:

struct my_function {
    my_function& operator()() {
        return *this;
    }
};

C では、void* をキャストする必要があります。これは妥当な実装であれば機能しますが、技術的には定義されていないと思います。

両方の言語で単純な再帰型が許可されるとよいでしょう。残念ながら、そうではありません。

于 2010-06-15T20:24:54.330 に答える
0

typedef に無限再帰があるため、互換性のある型はありません。

ただし、DrPizza のようなトリックを実行してシミュレートすることは可能です。ただし、文字通りそれを行うことはできません。

auto/decltype の奇跡によるサポート:

auto f() -> decltype(&f) { return &f; } = a shitload of errors.
于 2010-06-15T20:29:44.107 に答える
0

C コンパイラのバックエンドがそれを処理できないという理由はなく、幸運なフロント エンド (パーサーとシンボル解決パスを除く) も同様に処理できます。しかし、C が定義されている方法では、データをコンパイラの後のビットに渡すことは不可能です。参照を介してtypdefを実装し、使用するシンボルを解決する前にtypedefをシンボルテーブルに詰め込むのに十分な適合性がないCコンパイラのOTOHは、実際には問題なく飲み込みます。

于 2010-07-01T00:57:23.750 に答える