C ++自発的な考叀孊のセッションず、Cのスタむルで倉数関数を䜿甚しない理由

い぀ものように、すべおぱラヌから始たりたした。 Java Native Interfaceを䜿甚したのはこれが初めおで、C ++パヌトではJavaオブゞェクトを䜜成する関数をラップしたした。 この関数CallVoidMethodは可倉、぀たり JNI環境ぞのポむンタヌ、䜜成されるオブゞェクトの型ぞのポむンタヌ、および呌び出されたメ゜ッドこの堎合はコンストラクタヌの識別子に加えお、任意の数の他の匕数を取りたす。 論理的です、なぜなら これらの他の匕数は、Java偎の呌び出されたメ゜ッドに枡されたす。メ゜ッドは、任意のタむプの異なる数の匕数で異なるこずができたす。

したがっお、ラッパヌ倉数も䜜成したした。 CallVoidMethod任意の数の匕数をCallVoidMethod䜿甚しva_list 。この堎合は異なるためです。 はい、それはva_list送信したものCallVoidMethod 。 そしお、JVMの平凡なセグメンテヌション゚ラヌを削陀したした。

2時間で、8日から11日たでいく぀かのバヌゞョンのJVMを詊すこずができたした。これは、最初はこれがJVMの最初の経隓であり、この問題では自分よりもStackOverflowを信頌し、次に誰かがStackOverflowでは、この堎合、OpenJDKではなくOracleJDKを䜿甚し、8ではなく10を䜿甚するようにアドバむスしたした。そしお、倉数CallVoidMethodに加えお、 CallVoidMethodを介しお任意の数の匕数を取るCallVoidMethodがあるこずに最埌に気付きva_list 。

この話で䞀番気に入らなかったのは、省略蚘号ellipsisずva_list違いにすぐに気付かなかったこずva_list 。 そしお気づいたので、根本的な違いが䜕であるかを自分で説明できたせんでした。 そのため、省略蚘号、 va_list 、およびただC ++に぀いお話しおいるため倉数テンプレヌトを扱う必芁がありたす。

暙準の省略蚘号ずva_listに぀いおの説明


C ++暙準では、その芁件ず暙準Cの芁件ずの違いのみを説明しおいたす。違い自䜓に぀いおは埌で説明したすが、ここでは、暙準Cの内容C89以降に぀いお簡単に説明したす。


なんで しかし、なぜなら


Cには倚くの型はありたせん。 暙準でva_list宣蚀されおいるva_list 、内郚構造に぀いおは䜕も述べられおいないのはなぜですか

関数ぞの任意の数の匕数をva_listを介しおva_listこずができる堎合、なぜ省略蚘号が必芁なのですか 今は「構文糖ずしお」ず蚀えたすが、40幎前には、糖の時間はなかったず確信しおいたす。

フィリップゞェヌムスプラりガヌフィリップゞェヌムスプラりガヌは、 The Standard C library -1992-で、最初はCはPDP-11コンピュヌタヌ専甚に䜜成されたず述べおいたす。 たた、単玔なポむンタヌ挔算を䜿甚しお、関数のすべおの匕数を゜ヌトできたした。 この問題は、Cの人気ず他のアヌキテクチャヌぞのコンパむラヌの転送で発生したした。 ブラむアン・カヌニガンずデニス・リッチヌによるCプログラミング蚀語の初版-1978-には次のように明蚘されおいたす。
ちなみに、任意の数の匕数の移怍可胜な関数を䜜成する蚱容可胜な方法はありたせん。 呌び出された関数が、呌び出されたずきに枡された匕数の数を調べるための移怍可胜な方法はありたせん。 ... printf 、匕数の任意の数の最も兞型的なC蚀語関数、...は移怍性がなく、各システムに実装する必芁がありたす。
この本はprintfに぀いお説明しおいたすが、ただvprintfがなく、タむプずマクロva_*に぀いおは蚀及しおいたせん。 これらはCプログラミング蚀語の第2版1988幎に登堎し、これは最初のC暙準C89、別名ANSI Cの開発委員䌚のメリットです。 委員䌚は、UNIX OSの移怍性を高める目的でAndrew Koenigが䜜成した<varargs.h>基瀎ずしお、 <stdarg.h>を暙準に远加したした。 既存のコンパむラが新しい暙準をサポヌトしやすくするために、 va_*マクロをマクロずしお残すva_*が決定されたした。

珟圚、C89ずva_*ファミリヌの出珟により、移怍可胜な倉数関数を䜜成できるようになりたした。 そしお、このファミリヌの内郚構造はいただに蚘述されおおらず、芁件もありたせんが、その理由はすでに明らかです。

奜奇心から、 <stdarg.h>実装の䟋を芋぀けるこずができたす。 たずえば、同じ「C暙準ラむブラリ」はBorland Turbo C ++の䟋を提䟛したす 。

Borland Turbo C ++の<stdarg.h>
 #ifndef _STADARG #define _STADARG #define _AUPBND 1 #define _ADNBND 1 typedef char* va_list #define va_arg(ap, T) \ (*(T*)(((ap) += _Bnd(T, _AUPBND)) - _Bnd(T, _ADNBND))) #define va_end(ap) \ (void)0 #define va_start(ap, A) \ (void)((ap) = (char*)&(A) + _Bnd(A, _AUPBND)) #define _Bnd(X, bnd) \ (sizeof(X) + (bnd) & ~(bnd)) #endif 


AMD64甚のはるかに新しいSystemV ABIは 、 va_listこのタむプを䜿甚しva_list 。

SystemV ABI AMD64のva_list
 typedef struct { unsigned int gp_offset; unsigned int fp_offset; void *overflow_arg_area; void *reg_save_area; } va_list[1]; 


䞀般的に、型ずマクロva_*は、倉数関数の匕数をトラバヌスするための暙準むンタヌフェむスを提䟛し、歎史的な理由によるそれらの実装は、コンパむラ、タヌゲットプラットフォヌム、およびアヌキテクチャに䟝存するず蚀うこずができたす。 さらに、Cでは、省略蚘号぀たり、䞀般的な倉数関数がva_list ぀たり、ヘッダヌ<stdarg.h> よりも前に登堎したした。 たた、 va_listは省略蚘号を眮き換えるために䜜成されたのではなく、開発者が移怍可胜な倉数関数を䜜成できるようにするために䜜成されたした。

C ++は䞻にCずの埌方互換性を維持しおいるため、䞊蚘のすべおが適甚されたす。 しかし、機胜もありたす。

C ++の倉数関数


WG21ワヌキンググルヌプは、C ++暙準の開発に関䞎しおいたす。 1989幎に、新しく䜜成されたC89暙準が基瀎ずなり、C ++自䜓を蚘述するために埐々に倉曎されたした。 1995幎に、提案N0695が John Miccoから受け取られたした。著者は、マクロva_*制限を倉曎するこずを提案したした。

私は痛みを分かち合うために最埌のポむントを翻蚳したせんでした。 たず、C ++ Standardの「 デフォルトの匕数型の゚スカレヌション 」は[C ++ 17 8.2.2 / 9]のたたです。 そしお第二に、私はこのフレヌズの意味に぀いお長い間困惑し、すべおが明確なスタンダヌドCず比范したした。 N0695を読んだ埌にようやく理解できたのは、同じこずです。

ただし、3぀の倉曎すべおが採甚されたした[C ++ 98 18.7 / 3] 。 C ++に戻るず、少なくずも1぀の名前付きパラメヌタヌこの堎合、他のパラメヌタヌにはアクセスできたせんが、埌で詳しく説明したすを持぀倉数関数の芁件はなくなり、名前のない匕数の有効なタむプのリストは、クラスメンバヌおよびPODタむプぞのポむンタヌで補足されたした。

C ++ 03暙準では、倉数関数に倉曎はありたせんでした。 C ++ 11は、 std::nullptr_t型の名前のない匕数をvoid*に倉換し始め、コンパむラヌの裁量で、非自明なコンストラクタヌずデストラクタヌを持぀型をサポヌトできるようにしたした[C ++ 11 5.2.2 / 7] 。 C ++ 14では、最埌の名前付きパラメヌタヌずしお関数ず配列を䜿甚できるようになり[C ++ 14 18.10 / 3] 、C ++ 17では、ラムダによっおキャプチャされる拡匵パックず倉数の䜿甚が犁止されたした[C ++ 17 21.10.1 / 1] 。

その結果、C ++は萜ずし穎にさたざたな機胜を远加したした。 非自明なコンストラクタ/デストラクタで指定されおいない型サポヌトのみが䟡倀がありたす。 以䞋では、倉数関数のすべおの非自明な機胜を1぀のリストにたずめ、特定の䟋で補足しようずしたす。

倉数関数を簡単か぀誀っお䜿甚する方法


  1. 昇栌された型で最埌の名前付き匕数を宣蚀するのは間違っおいたす。 char 、 signed char 、 unsigned char 、 singed short 、 unsigned shortたたはfloat 暙準に埓った結果は、未定矩の動䜜になりたす。

    無効なコヌド
     void foo(float n, ...) { va_list va; va_start(va, n); std::cout << va_arg(va, int) << std::endl; va_end(va); } 


    手元にあるすべおのコンパむラgcc、clang、MSVCのうち、 clangのみが譊告を発行したした。

    クラン譊告
     ./test.cpp:7:18: warning: passing an object that undergoes default argument promotion to 'va_start' has undefined behavior [-Wvarargs] va_start(va, n); ^ 

    そしお、すべおの堎合においお、コンパむルされたコヌドは正しく動䜜したしたが、それを圓おにするべきではありたせん。

    正しいでしょう
     void foo(double n, ...) { va_list va; va_start(va, n); std::cout << va_arg(va, int) << std::endl; va_end(va); } 

  2. 最埌の名前付き匕数を参照ずしお宣蚀するのは正しくありたせん。 任意のリンク。 この堎合の暙準は、未定矩の動䜜も保蚌したす。

    無効なコヌド
     void foo(int& n, ...) { va_list va; va_start(va, n); std::cout << va_arg(va, int) << std::endl; va_end(va); } 

    gcc 7.3.0は、単䞀のコメントなしでこのコヌドをコンパむルしたした。 lang6.0.0は譊告を発行したしたが、それでもコンパむルしたした。

    クラン譊告
     ./test.cpp:7:18: warning: passing an object of reference type to 'va_start' has undefined behavior [-Wvarargs] va_start(va, n); ^ 

    どちらの堎合も、プログラムは正しく機胜したした幞運なこずに、あなたはそれに頌るこずはできたせん。 しかし、 MSVC 19.15.26730はそれ自䜓を際立たせおいたす-コヌドのコンパむルを拒吊したした。 va_start匕数va_start参照であっおはなりva_startん。

    MSVCからの゚ラヌ
     c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\vadefs.h(151): error C2338: va_start argument must not have reference type and must not be parenthesized 

    さお、正しいオプションは、たずえば次のようになりたす
     void foo(int* n, ...) { va_list va; va_start(va, n); std::cout << va_arg(va, int) << std::endl; va_end(va); } 

  3. va_arg type- char 、 shortたたはfloat va_arg䞊げるように芁求するのは間違っおいたす。

    無効なコヌド
     #include <cstdarg> #include <iostream> void foo(int n, ...) { va_list va; va_start(va, n); std::cout << va_arg(va, int) << std::endl; std::cout << va_arg(va, float) << std::endl; std::cout << va_arg(va, int) << std::endl; va_end(va); } int main() { foo(0, 1, 2.0f, 3); return 0; } 

    ここでもっず面癜いです。 コンパむル時のgccは、 floatではなくdoubleを䜿甚する必芁があるずいう譊告を出したす。このコヌドがただ実行されおいる堎合、プログラムぱラヌで終了したす。

    GCC譊告
     ./test.cpp:9:15: warning: 'float' is promoted to 'double' when passed through '...' std::cout << va_arg(va, float) << std::endl; ^~~~~~ ./test.cpp:9:15: note: (so you should pass 'double' not 'float' to 'va_arg') ./test.cpp:9:15: note: if this code is reached, the program will abort 

    確かに、プログラムは無効な呜什に関する苊情でクラッシュしたす。
    ダンプ分析は、プログラムがSIGILLシグナルを受信したこずを瀺しおいたす。 たた、 va_listの構造も瀺しおいたす。 32ビットの堎合、これは

     va = 0xfffc6918 "" 

    ぀たり va_listは単なるchar*です。 64ビットの堎合

     va = {{gp_offset = 16, fp_offset = 48, overflow_arg_area = 0x7ffef147e7e0, reg_save_area = 0x7ffef147e720}} 

    ぀たり SystemV ABI AMD64で説明されおいるずおりです。

    コンパむル時のclangは、未定矩の動䜜を譊告し、 floatをdouble眮き換えるこずを提案したす。

    クラン譊告
     ./test.cpp:9:26: warning: second argument to 'va_arg' is of promotable type 'float'; this va_arg has undefined behavior because arguments will be promoted to 'double' [-Wvarargs] std::cout << va_arg(va, float) << std::endl; ^~~~~ 

    しかし、プログラムがクラッシュするこずはなくなり、32ビットバヌゞョンでは以䞋が生成されたす。

     1 0 1073741824 

    64ビット

     1 0 3 

    MSVCは、 /Wallあっおも、譊告なしでのみたったく同じ結果を生成したす。

    ここで、32ビットず64ビットの違いは、最初のケヌスではABIがすべおの匕数をスタック経由で呌び出された関数に枡し、2番目では最初の4぀Windowsたたは6぀Linuxの匕数がプロセッサレゞスタを通過し、残りがスタック[ wiki ]。 ただし、4぀の匕数ではなく19でfooを呌び出しお同じ方法で出力するず、結果は同じになりたす。32ビットバヌゞョンでは完党に混乱し、64ビットバヌゞョンではすべおのfloatがれロになりたす。 ぀たり ポむントはもちろんABIですが、匕数を枡すためにレゞスタを䜿甚するこずではありたせん。

    たあ、もちろん、そうするために
     void foo(int n, ...) { va_list va; va_start(va, n); std::cout << va_arg(va, int) << std::endl; std::cout << va_arg(va, double) << std::endl; std::cout << va_arg(va, int) << std::endl; va_end(va); } 

  4. 名前のない匕数ずしお重芁なコンストラクタたたはデストラクタを持぀クラスのむンスタンスを枡すこずは正しくありたせん。 もちろん、このコヌドの運呜が「今ここでコンパむルしお実行する」以䞊にあなたを興奮させない限りは。

    無効なコヌド
     #include <cstdarg> #include <iostream> struct Bar { Bar() { std::cout << "Bar default ctor" << std::endl; } Bar(const Bar&) { std::cout << "Bar copy ctor" << std::endl; } ~Bar() { std::cout << "Bar dtor" << std::endl; } }; struct Cafe { Cafe() { std::cout << "Cafe default ctor" << std::endl; } Cafe(const Cafe&) { std::cout << "Cafe copy ctor" << std::endl; } ~Cafe() { std::cout << "Cafe dtor" << std::endl; } }; void foo(int n, ...) { va_list va; va_start(va, n); std::cout << "Before va_arg" << std::endl; const auto b = va_arg(va, Bar); va_end(va); } int main() { Bar b; Cafe c; foo(1, b, c); return 0; } 

    再びClangがより厳栌になりたした。 圌va_arg 、 va_argの2番目の匕数がPODタむプでva_argないため、このコヌドのコンパむルを拒吊し、プログラムが起動時にva_argするこずを譊告したす。

    クラン譊告
     ./test.cpp:23:31: error: second argument to 'va_arg' is of non-POD type 'Bar' [-Wnon-pod-varargs] const auto b = va_arg(va, Bar); ^~~ ./test.cpp:31:12: error: cannot pass object of non-trivial type 'Bar' through variadic function; call will abort at runtime [-Wnon-pod-varargs] foo(1, b, c); ^ 

    ただ-Wno-non-pod-varargsフラグを䜿甚しおコンパむルするず、そうなりたす。

    MSVCは、この堎合に重芁なコンストラクタヌを䜿甚するこずは移怍性がないず譊告したす。

    MSVCからの譊告
     d:\my documents\visual studio 2017\projects\test\test\main.cpp(31): warning C4840:    "Bar"          

    ただし、コヌドは正しくコンパむルおよび実行されたす。 コン゜ヌルで以䞋が取埗されたす。

    打ち䞊げ結果
     Bar default ctor Cafe default ctor Before va_arg Bar copy ctor Bar dtor Cafe dtor Bar dtor 

    ぀たり コピヌはva_argを呌び出したずきにのみ䜜成され、匕数は参照によっお枡されたす。 どういうわけか自明ではありたせんが、暙準では蚱可されおいたす。

    gcc 6.3.0は 、単䞀のコメントなしでコンパむルしたす。 出力は同じです

    打ち䞊げ結果
     Bar default ctor Cafe default ctor Before va_arg Bar copy ctor Bar dtor Cafe dtor Bar dtor 

    gcc 7.3.0も䜕に぀いおも譊告したせんが、動䜜は倉化しおいたす

    打ち䞊げ結果
     Bar default ctor Cafe default ctor Cafe copy ctor Bar copy ctor Before va_arg Bar copy ctor Bar dtor Bar dtor Cafe dtor Cafe dtor Bar dtor 

    ぀たり このバヌゞョンのコンパむラは匕数を倀で枡し、呌び出されるず、 va_argは別のコピヌを䜜成したす。 コンストラクタ/デストラクタに副䜜甚がある堎合、gccの6番目から7番目のバヌゞョンに切り替えるずきにこの違いを探すのは楜しいでしょう。

    ずころで、クラスぞの参照を明瀺的に枡し、芁求する堎合

    別の間違ったコヌド
     void foo(int n, ...) { va_list va; va_start(va, n); std::cout << "Before va_arg" << std::endl; const auto& b = va_arg(va, Bar&); va_end(va); } int main() { Bar b; Cafe c; foo(1, std::ref(b), c); return 0; } 

    すべおのコンパむラが゚ラヌをスロヌしたす。 暙準で芁求されおいるずおり。

    䞀般的に、本圓にしたい堎合は、匕数をポむンタで枡すほうが良いでしょう。

    このように
     void foo(int n, ...) { va_list va; va_start(va, n); std::cout << "Before va_arg" << std::endl; const auto* b = va_arg(va, Bar*); va_end(va); } int main() { Bar b; Cafe c; foo(1, &b, &c); return 0; } 


オヌバヌロヌド解決ず倉数関数


䞀方では、すべおが単玔です。暙準たたはナヌザヌ定矩の型倉換の堎合でも、省略蚘号ずの䞀臎は、通垞の名前付き匕数ずの䞀臎よりも劣りたす。

過負荷の䟋
 #include <iostream> void foo(...) { std::cout << "C variadic function" << std::endl; } void foo(int) { std::cout << "Ordinary function" << std::endl; } int main() { foo(1); foo(1ul); foo(); return 0; } 


打ち䞊げ結果
 $ ./test Ordinary function Ordinary function C variadic function 

ただし、これは、匕数なしのfooの呌び出しを個別に考慮しない限り機胜したす。

匕数なしでfooを呌び出す
 #include <iostream> void foo(...) { std::cout << "C variadic function" << std::endl; } void foo() { std::cout << "Ordinary function without arguments" << std::endl; } int main() { foo(1); foo(); return 0; } 

コンパむラヌ出力
 ./test.cpp:16:9: error: call of overloaded 'foo()' is ambiguous foo(); ^ ./test.cpp:3:6: note: candidate: void foo(...) void foo(...) ^~~ ./test.cpp:8:6: note: candidate: void foo() void foo() ^~~ 

すべおは暙準に準拠しおいたす。匕数はありたせん-省略蚘号ずの比范はありたせん。たた、オヌバヌロヌドが解決されるず、倉分関数は通垞のものより悪くなりたせん。

それにもかかわらず、倉数関数を䜿甚する䟡倀があるのはい぀ですか


さお、倉量関数は時々非垞に明確に振る舞わず、C ++のコンテキストでは移怍性が䜎いこずが容易に刀明したす。 「倉数C関数を䜜成たたは䜿甚しないでください」など、むンタヌネットには倚くのヒントがありたすが、C ++暙準からのサポヌトは削陀されたせん。 これらの機胜にはいく぀かの利点がありたすか よくそこに。


バリアントテンプレヌトたたは珟代のC ++で任意の数の匕数から関数を䜜成する方法


可倉テンプレヌトのアむデアは、2004幎にDouglas Gregor、JakkoJÀrvi、Gary Powellによっお提案されたした。 これらの倉数テンプレヌトが公匏にサポヌトされたC ++ 11暙準が採甚される7幎前。 この芏栌には、提案の第3版であるN2080が含たれおいたす。

プログラマヌが任意の数の匕数からタむプセヌフおよびポヌタブル関数を䜜成できるように、最初から倉数テンプレヌトが䜜成されたした。別の目暙は、可倉数のパラメヌタヌを持぀クラステンプレヌトのサポヌトを簡玠化するこずですが、珟圚は可倉機胜に぀いおのみ説明しおいたす。

倉数テンプレヌトは、C ++ [C ++ 17 17.5.3]に 3぀の新しい抂念をもたらしたした。


䟋
 template <class ... Args> void foo(const std::string& format, Args ... args) { printf(format.c_str(), args...); } 

class ... Args — , Args ... args — , args... — .

パラメヌタパッケヌゞを展開できる堎所ず方法の完党なリストは、暙準自䜓[C ++ 17 17.5.3 / 4]に蚘茉されおいたす。そしお、倉数関数の議論の文脈では、それを蚀うだけで十分です


明瀺的な省略蚘号パッケヌゞを開瀺する際に、さたざたなテンプレヌトサポヌトするために必芁ずされるパタヌン開瀺をし、この曖昧さを避けるために。

䟋えば
 template <class ... Args> void foo() { using OneTuple = std::tuple<std::tuple<Args>...>; using NestTuple = std::tuple<std::tuple<Args...>>; } 

OneTuple — ( std:tuple<std::tuple<int>>, std::tuple<double>> ), NestTuple — , — ( std::tuple<std::tuple<int, double>> ).

倉数テンプレヌトを䜿甚したprintfの実装䟋


すでに述べたように、倉数テンプレヌトはCの倉数関数の盎接の代替ずしおも䜜成されたした。これらのテンプレヌトの䜜成者自身は、非垞にシンプルでありながらタむプセヌフなバヌゞョンを提案しprintfたした。

テンプレヌトのprintf
 void printf(const char* s) { while (*s) { if (*s == '%' && *++s != '%') throw std::runtime_error("invalid format string: missing arguments"); std::cout << *s++; } } template <typename T, typename ... Args> void printf(const char* s, T value, Args ... args) { while (*s) { if (*s == '%' && *++s != '%') { std::cout << value; return printf(++s, args...); } std::cout << *s++; } throw std::runtime_error("extra arguments provided to printf"); } 

疑わしいのは、倉数匕数の列挙のこのパタヌンが珟れた-オヌバヌロヌドされた関数の再垰呌び出しを通しお。しかし、私はただ再垰のないオプションを奜みたす。

テンプレヌト䞊のprintfず再垰なし
 template <typename ... Args> void printf(const std::string& fmt, const Args& ... args) { size_t fmtIndex = 0; size_t placeHolders = 0; auto printFmt = [&fmt, &fmtIndex, &placeHolders]() { for (; fmtIndex < fmt.size(); ++fmtIndex) { if (fmt[fmtIndex] != '%') std::cout << fmt[fmtIndex]; else if (++fmtIndex < fmt.size()) { if (fmt[fmtIndex] == '%') std::cout << '%'; else { ++fmtIndex; ++placeHolders; break; } } } }; ((printFmt(), std::cout << args), ..., (printFmt())); if (placeHolders < sizeof...(args)) throw std::runtime_error("extra arguments provided to printf"); if (placeHolders > sizeof...(args)) throw std::runtime_error("invalid format string: missing arguments"); } 

オヌバヌロヌド解決および倉数テンプレヌト関数


解決するずき、これらの倉化関数は、他の埌に、テンプレヌトずしお、最も専門性の䜎いものず芋なされたす。ただし、匕数なしの呌び出しの堎合は問題ありたせん。

過負荷の䟋
 #include <iostream> void foo(int) { std::cout << "Ordinary function" << std::endl; } void foo() { std::cout << "Ordinary function without arguments" << std::endl; } template <class T> void foo(T) { std::cout << "Template function" << std::endl; } template <class ... Args> void foo(Args ...) { std::cout << "Template variadic function" << std::endl; } int main() { foo(1); foo(); foo(2.0); foo(1, 2); return 0; } 

打ち䞊げ結果
 $ ./test Ordinary function Ordinary function without arguments Template function Template variadic function 

過負荷が解決されるず、倉数テンプレヌト関数は倉数C関数のみをバむパスできたすなぜそれらを混圚させるのですか。䟋倖-もちろん-匕数なしで呌び出したす。

匕数なしで呌び出す
 #include <iostream> void foo(...) { std::cout << "C variadic function" << std::endl; } template <class ... Args> void foo(Args ...) { std::cout << "Template variadic function" << std::endl; } int main() { foo(1); foo(); return 0; } 

打ち䞊げ結果
 $ ./test Template variadic function C variadic function 

省略蚘号ずの比范がありたす-察応する関数は倱われ、省略蚘号ずの比范はありたせん-テンプレヌト関数は非テンプレヌト関数よりも劣っおいたす。

可倉テンプレヌト関数の速床に関する簡単なメモ


2008幎、ロむックゞョリヌは提案N2772をC ++暙準化委員䌚に提出したした。そこで、倉数テンプレヌト関数は、匕数が初期化リストstd::initializer_listである同様の関数よりも動䜜が遅いこずを実際に瀺したした。これは䜜者自身の理論的実蚌に反したもののず、ゞョリヌは、実装するこずを提案しstd::min、std::maxそしおstd::minmaxそれは、初期化リストの代わりの倉圢パタヌンを介しお行われたす。

しかし、すでに2009幎に反論がありたした。ゞョリのテストで、「重倧な間違い」が発芋されたした自分自身にさえ思えたす。新しいテストこちらずこちらをご芧くださいは、倉数テンプレヌト関数がさらに高速で、堎合によっおはかなり高速であるこずを瀺したした。驚くこずではありたせん 初期化リストはその芁玠のコピヌを䜜成したす。倉数テンプレヌトの堎合、コンパむル段階で倚くをカりントできたす。

それにもかかわらず、C ++ 11以降の暙準std::minではstd::max、std::minmaxは通垞のテンプレヌト関数であり、初期化リストを介しお枡される任意の数の匕数です。

簡単な芁玄ず結論


したがっお、Cスタむルの倉数関数


倉数関数の唯䞀の䜿甚は、C ++コヌドでC APIず察話するこずです。SFINAEを含む他のすべおに぀いお、以䞋の倉数テンプレヌト関数がありたす。


倉数テンプレヌト関数は、Cスタむルの察応する関数ず比范しおより冗長である堎合があり、時には独自のオヌバヌロヌドされた非テンプレヌトバヌゞョン再垰的な匕数トラバヌサルを必芁ずするこずさえありたす。読み取りず曞き蟌みが困難です。しかし、これはすべお、リストされおいる欠点がなく、リストされおいる利点が存圚するこずで、察䟡が支払われたす。

結論は簡単です。Cスタむルのバリ゚ヌション関数は、䞋䜍互換性のためだけにC ++のたたであり、脚を撃぀ための幅広いオプションを提䟛したす。珟代のC ++では、新しいものを蚘述しないこず、できれば既存の倉数C関数を䜿甚しないこずを匷くお勧めしたす。可倉テンプレヌト関数は、最新のC ++の䞖界に属し、はるかに安党です。それらを䜿甚したす。

文献ず情報源



PS


䞊蚘の曞籍の電子版をネット䞊で簡単に芋぀けおダりンロヌドできたす。しかし、それが合法かどうかはわかりたせんので、リンクは提䟛したせん。

Source: https://habr.com/ru/post/J430064/


All Articles