10

C/C++ で別の関数への関数ポインタを返す汎用関数を作成したいと考えています。ただし、2 番目に返される関数は、最初の関数の変数を使用できる必要があります。

例、

typedef double (*func_t)(double);

func_t inverse(func_t fn) {
   // define another function here that uses fn
   double solve(double x) {
      // use fn
   }
   return &solve;
}

double sqr(double x) { return x * x; }

int main() {
     func_t inv = inverse(sqr);
     printf("sqrt %d = %f\n", 100, inv(100)); 
}

明らかに、gcc、g++ ではこれを行うことができません。クラスや構造体を使わずにこれを達成できますか?

4

8 に答える 8

5

これはネストされた関数に関するものではありません。これは閉鎖についてです。GCC のネストされた関数は、質問が求めていることを実行しない、半分実装された形式のクロージャーです。

C は、どの標準でも、クロージャをまったくサポートしていません。

C++11 はラムダによるクロージャーをサポートしており、C++ に固有のソリューションが他にもいくつかあります。

Apple の Clang コンパイラ、「ブロック」の形式で、C モードの拡張機能としてクロージャをサポートしています。ネストされた関数とは異なり、これらは実際には要求されたユース ケースで機能します (つまり、より高い呼び出しレベルに戻されます)。

次のように書きます。

typedef double (^func_t)(double);  // block type is slightly different from function pointer

func_t inverse(func_t fn) {
   return ^ double(double x) {
       //use fn
   };
}

// ...etc., same from here

しかし、クロージャーを広範囲に使用したい場合は、別の言語を使用する必要があります。C では、メモリ管理がまったく行われていないため、深刻な問題が発生します (制限のないクロージャを手動で管理するのは悪夢です)。

于 2013-10-22T20:39:39.073 に答える
4

これは、ラムダを使用する C++11 で可能です。

#include <cstdio>
#include <cmath>
#include <functional>

typedef std::function<double(double)> func_t;

func_t inverse(func_t fn) {
    return [=](double x) { printf("fn(x): %f\n", fn(x)); return sqrt(x); };
}

double sqr(double x) { return x * x; }

int main() {
    auto inv = inverse(sqr);
    printf("sqrt %d = %f\n", 100, inv(100));
}
于 2013-10-22T19:39:27.670 に答える
4

Cの答え:

C はネストされた関数 (別の関数内で関数を定義する) をサポートしていませんがgcc、C の GNU 拡張機能として使用できます。

http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html

于 2013-10-22T19:27:54.080 に答える
2

C++11 を使用できる場合、それらのラムダ関数はあなたの友達です。

#include <functional>
#include <iostream>

std::function<double(double)> identity(std::function<double(double)> fn)
{
  return [fn](double x){
    // Do your logic here
    return fn(x);
  };
}

double sqr(double x) { return x * x; }

int main() {
  auto f = identity(sqr);
  std::cout << "sqr(100) = " << f(100) << std::endl;
}

C++11 をサポートする予定がない場合は、次の方法で実行できます。

#include <iostream>

typedef double (*func_t)(double);
struct func
{
  func_t fn;
  double (*calc)(func_t, double);
  double operator()(double x) {return calc(fn, x);}
};

double identity_calc(func_t fn, double x) {return fn(x);}

func identity(func_t fn)
{
  func f = {fn, identity_calc};
  return f;
}

double sqr(double x) { return x * x; }

int main() {
  func f = identity(sqr);
  std::cout << "sqr(100) = " << f(100) << std::endl;
}

しかし、普通の C ではうまくいかないと思います。

于 2013-10-22T19:53:36.547 に答える
1

comp.lang.c FAQ リスト · 質問 20.24 :

ネストされた関数を実装して、含まれている関数のローカル変数に適切にアクセスできるようにすることは簡単ではないため、単純化のために意図的に C から除外されました。(gcc では、拡張機能としてそれらを許可しています。) ネストされた関数 (qsort 比較関数など) の多くの潜在的な用途について、少し面倒ではあるが適切な解決策は、静的宣言を持つ隣接する関数を使用し、必要に応じていくつかの静的変数を介して通信することです。(よりクリーンな解決策は、qsort ではサポートされていませんが、必要なコンテキストを含む構造体へのポインターを渡すことです。)

于 2013-10-22T19:57:06.360 に答える
1

c の関数内で関数を定義しますか?

入れ子関数と呼ばれます。

ネストされた関数はANSI Cの一部ではありませんが、Gnu Cの一部です

ウィキペディアより

字句的にネストされた関数をサポートするよく知られた言語には、次のものがあります。

ALGOL 68、Simula、Pascal、Modula-2、Modula-3、Oberon、Seed7、Ada などの ALGOL ベースの言語。

Scheme や Common Lisp などの最新バージョンの Lisp (レキシカル スコープ付き)。

ECMAScript (JavaScript、および ActionScript)。

Scala (フルサポート)

Ruby、Python、Perl (バージョン 6 以降) などのスクリプト言語のさまざまなレベルのサポート。

標準 C および C++ はネストされた関数をサポートしていませんが、GCC は言語拡張として C のネストされた関数をサポートしています。

C 関連の D 言語には、ネストされた関数があります。

Fortran-90 以降の Fortran では、1 レベルのネストされた (CONTAINed) サブルーチンと関数がサポートされています。

MATLAB (完全サポート)

GNU C 構文の例 (ネストされた関数で C 拡張):

float E(float x)
{
    float F(float y)
    {
        return x + y;
    }
    return F(3) + F(4);
}
于 2013-10-22T19:31:02.773 に答える
-1

通常、追加のファイル/モジュールに関数を記述します。内部関数を静的にすると、外部の誰もそれを使用できなくなります。

製造

于 2013-10-22T19:35:01.950 に答える