C ++の入れ子関数

コミュニティへのご挨拶!



関数内で関数を宣言するのと似たようなことをC ++で行う機会に出会いました。 次のようになります。



#include <iostream> int main() { inline_function(std::string s) { std::cout << "Hello, " << s << "!\n"; } with_name(make_hello); make_hello("Vasiliy Pupkin!"); return 0; }
      
      







上記の例では、mainメソッド内で、ネストされた「メソッド」がmake_helloという名前で作成され、次にパラメーター「Vasiliy Pupkin」で呼び出されます。 もちろん、 Hello, Vasiliy Pupkin!







残念ながら、名前を上にドラッグできませんでした。







もちろん、これはマクロで行われます。



マクロなしの同じ例





C ++では、ネストされた関数はまだ存在しないため、他の構文を使用して構文をエミュレートする必要があります。 したがって、私たちは別の質問をします:関数とはまったく同じように見えますが、関数とはまったく同じではないものは何ですか?



2つの答えがあります。まず、オブジェクトを「voidに」作成せずにクラスのコンストラクターを呼び出します。



 #include <stdio.h> int main() { //        class inline_function { public: //          inline_function() { printf("Hello, hell!\n"); } }; //      inline_function(); return 0; }
      
      







残念ながら、実際に特定の例を使用することを許可しない2つの重要な「しかし」があります。



1.関数呼び出しごとに1つの新しいオブジェクトを作成しますが、これは良くありません。 さて、どのように多くの呼び出しを循環させるのですか? オーバーヘッドコストは、そのようなことの宣言の事実については許容できますが、使用については許容できません。



2.コンパイラの最適化が有効になっているVisual Studioは、inline_function()の作成を無駄にカットするだけです。 コンパイラのロジックは理解できます(とにかく、誰もこの作成されたオブジェクトを使用しないので、なぜそれを作成しますか?)しかし、それは危険です。



ただし、「演算子の再定義」と呼ばれるものがまだあります。二重括弧を再定義できます(スマートに再定義された演算子()は「ファンクター」と呼ばれます)。



 #include <stdio.h> int main() { //        class inline_function_class { public: void operator()() { printf("Hello, world!\n"); } }; //       --   ! inline_function_class inline_function; //    inline_function(); return 0; }
      
      







この例では、クラスを匿名にすることができるため、ほとんどすべてがすでに問題ありません。そのため、型inline_function_class



名前をinline_function_class



する必要はありません。



 #include <stdio.h> int main() { //        class { public: void operator()() { printf("Hello, world!\n"); } } inline_function; //       //    inline_function(); return 0; }
      
      







マクロを書く



そのため、原則として、目標が達成されました。外部の可視性ゾーンを損なわないように、1つのメソッド内でコードを分離する方法があります。 しかし、マイナス点が1つあります。与えられたすべての例では、私の好みのために、文字が多すぎます。 したがって、コードを3つの部分に分割します。



 #include <stdio.h> int main() { //        /**   --   ,   ,     : **/ class { public: void operator()(/*       */) { /**  -- ,  ,     **/ printf("Hello, world!\n"); /**   --    **/ } } inline_function; //       /** =============================== **/ //    inline_function(); return 0; }
      
      







最初と2番目の部分は2つのマクロに詰め込まれ、それらにいくつかの素敵な名前を付けます。 例えば

 #define inline_function(params) \ class \ { \ public: void operator() (params)\ {\ #define with_name(value) \ }\ } value; #define with_params(...) __VA_ARGS__ //  -  ?  .
      
      







これらのマクロを使用すると、「ネストされた関数」のコードの外観が大幅に簡素化されます(ただし、C ++スタイルでは表示されません)。

 int main() { inline_function(char * name) { printf("Hello, %s!\n",name); } with_name(hello) hello("Pupkin"); }
      
      







残念ながら、関数に1つではなく少なくとも2つのパラメーターを設定しようとすると、すべてがうまくいきません。 この場合、コンパイラはクラッシュします、彼らは、inline_functionマクロには1つのパラメータとバスタだけがあると言います。 この問題は__VA_ARGS__マクロを使用することで解決できます(マクロは標準の一部ではありませんが、実際に使用されているすべてのコンパイラでサポートされています)。



2つのパラメーターを持つ、もう少し複雑ですが、まだ許容できる関数は次のようになります。

 int main() { inline_function (with_params(int a, int b)) { printf("%d+%d=%d\n",a,b,a+b); } with_name(plus); plus(2,2); }
      
      







結論として、 これらのマクロには戻り値がないことに注意してください 。 もちろん、それは非常に簡単に「投げ捨てられる」可能性があるので、読者にお任せします。



PS実際、ネストされたクラスを作成する手法は世界と同じくらい古いものであり、もちろん、私は新しいことを発見しませんでした。 それにもかかわらず、そのような記事は教育的な意味で有用であり、美的な完全性を持っているように思えます。



All Articles