5

いくつかの副作用を実行してから答えを返す関数があるとします。

int foo()
{
    perform_some_side_effect();
    return 42;
}

関数ポインタにバインドしたいfooのですが、答えには興味がなく、副作用だけです。

void (*bar)() = foo;

ただし、これはタイプエラーのようです。

error: invalid conversion from ‘int (*)()’ to ‘void (*)()’

そのエラーの背後にある理論的根拠は何ですか?型システムで答えを無視できないのはなぜですか?


ちなみに、関数ポインタをstd::function:でラップすると機能します。

std::function<void()> baz = foo;

std::function型システムでこの制限を(どうやら)回避する方法はありますか?

4

4 に答える 4

9

そのエラーの背後にある理論的根拠は何ですか?型システムで答えを無視できないのはなぜですか?

その理由は、タイプが異なり、呼び出し場所(関数ポインターを介して)で生成されるコードが異なるためです。すべての引数がスタックに書き込まれ、戻り値のスペースもスタックに予約される呼び出し規約について考えてみます。呼び出しがaを通過した場合void (*)()、戻り値のためにスタックにスペースは予約されませんが、関数(呼び出し方法を認識しない)は42、呼び出し元が予約されたスペースを持つべき場所にを書き込みます。

std :: functionは(どうやら)型システムでこの制限を回避することができますか?

そうではありません。実際の関数への呼び出しをラップする関数オブジェクトを作成します。次のようなメンバーが含まれます。

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

これで、コンパイラが呼び出しを処理するときに、を返す関数を呼び出すfooためにコンパイラが何をしなければならないかがわかり、呼び出し規約に従って実行されます。テンプレートは返されないため、実際に返された値を無視します。int

于 2012-06-03T01:22:47.540 に答える
1

他の人が言っていることに加えて、呼び出し元は、結果に対してどのデストラクタを呼び出す必要があるかを知るために、returnタイプも必要です(戻り値は一時的なものである可能性があります)。


残念ながら、それはそれほど簡単ではありません

auto (*bar)() = foo; 

GCCとClangはこれを受け入れますが。仕様を再確認して、それが実際に正しいかどうかを確認する必要があります。

更新:仕様によると

auto type-specifierは、宣言されている変数の型がその初期化子から推定されること、または関数宣言子が末尾の戻り型を含むことを意味します。

これは高速で読み取ると誤解を招く可能性がありますが、これはGCCとclangによって実装され、トップレベルの宣言子にのみ適用されます。私たちの場合、これはポインター宣言子です。その中にネストされている宣言子は関数宣言子です。したがって、代わりautoに使用するvoidと、コンパイラが型を推測します。


ちなみに、これはいつでも手動で機能させることができますが、機能させるには多少の注意が必要です

template<typename FunctionType>
struct Params; 

template<typename ...Params>
struct Params<void(Params...)> {
  template<typename T>
  using Identity = T;

  template<typename R>
  static Identity<R(Params...)> *get(R f(Params...)) {
    return f;
  }
};

// now it's easy
auto bar = Params<void()>::get(foo);
于 2012-06-03T11:19:17.260 に答える
1

std::functionソース互換である必要があるだけです。つまり、結果を無視する新しい校正コードを生成する新しいクラスを生成できます。関数ポインタはバイナリ互換である必要があり、そのジョブを実行できず、まったく同じコードvoid(*)()を指すことはできません。int(*)()

于 2012-06-02T23:45:05.743 に答える
1

std::function<>あなたはあなたの特定のケースのためにこれをすることを考えることができます:

void __func_void()
{
    foo();
}

実際にはそれよりも少し複雑ですが、要点は、詳細を気にせずに型消去と一緒にテンプレートコードを生成することです。

于 2012-06-02T23:46:24.620 に答える