Variadic テンプレートまたは 3 つのドットの累乗

可変個引数テンプレートは、任意の数のテンプレート パラメーターを持つことができるテンプレートです。初めて見た場合、この機能は魔法のように見えるかもしれません。それでは、可変個引数テンプレートを分かりやすく説明しましょう。

私が書いているトピックを示す私のグラフィックに、テンプレートのインスタンス化が含まれていることを不思議に思うかもしれません。理由は簡単です。 「テンプレートのインスタンス化」に関する私の最後の投稿の後、私のドイツ人の読者の 1 人 (仮名 Urfahraner Auge) がコメントを書きました。テンプレートの暗黙的なインスタンス化と明示的なインスタンス化には重要な違いがありますが、これについて言及するのを忘れていました。彼は正しい。テンプレートの暗黙的なインスタンス化は遅延ですが、テンプレートの明示的なインスタンス化は積極的です。

Lazy 対 Eager テンプレートのインスタンス化

テンプレートのインスタンス化は怠惰です。 つまり、クラス テンプレートのメンバー関数が必要ない場合、インスタンス化されません。メンバー関数の宣言のみが利用可能で、その定義は利用できません。これは、メンバー関数で無効なコードを使用できる限り機能します。もちろん、メンバー関数を呼び出してはなりません。

// numberImplicitExplicit.cpp

#include <cmath>
#include <string>

template <typename T>
struct Number {
 int absValue() {
 return std::abs(val);
 }
 T val{};
};

// template class Number<std::string>; // (2)
// template int Number<std::string>::absValue(); // (3)

int main() {
 
 Number<std::string> numb;
 // numb.absValue(); // (1)
 
}

メンバー関数 numb.absValue() を呼び出す場合 (1 行目)、期待どおりの結果が得られます。本質的にオーバーロードがないことを示すコンパイル時のエラー メッセージ std::abs std::string の場合 利用可能。以下は、詳細なエラー メッセージの最初の 2 行です:

テンプレートのインスタンス化をもっと正確に説明する必要があります。 テンプレートの暗黙的なインスタンス化は遅延ですが、テンプレートの明示的なインスタンス化は熱心です。

行 (2) を有効にすると (template class Number<std::string> ) を作成し、クラス テンプレート Number を明示的にインスタンス化します。 または、行 (3) を有効にします (template int Number<std::string>::absValue( )) メンバー関数 absValue を明示的にインスタンス化 std::string の場合 、コンパイル時エラーが発生します。このコンパイル時エラーは、行 (1) でメンバ関数 absValue を呼び出すコンパイラ エラーと同等です (numb.absValue() )。行 (2) または行 (3) を有効にした後のエラー メッセージの最初の 2 行をもう一度示します。

  • 行 (2) が有効

  • 行 (3) が有効

個人的なメモ:

私は自分の投稿についてコメントをもらうことに熱心です。あなたが聞きたい内容について書くのを手伝ってくれます。特に、ドイツのコミュニティは非常に熱心です。

さて、最後にまったく別のもの、可変個引数テンプレートです。

Variadic テンプレート

可変個引数テンプレートは、任意の数のテンプレート パラメーターを持つことができるテンプレートです。初めて見た場合、この機能は魔法のように思えるかもしれません。

template <typename ... Args>
void variadicTemplate(Args ... args) { 
 . . . . // four dots
}

省略記号 (... ) は Args になります または args いわゆるパラメータパック。正確には、Args はテンプレート パラメータ パックであり、args 関数パラメーターパックです。パラメータパックで2つの操作が可能です。梱包および開梱できます。楕円が Args の左側にある場合 Args の右側にある場合、パラメーター パックはパックされます。 、開梱されています。関数テンプレート引数推定により、コンパイラはテンプレート引数を導出できます。

可変個引数テンプレートは、標準テンプレート ライブラリやコア言語でもよく使用されます。

template <typename... Types> // (1)
class tuple; 

template <typename Callable, typename... Args > // (2)
explicit thread(Callable&& f, Args&&... args); 

template <typename Lockable1, typename Lockable2, typename... LockableN> // (3)
void lock(Lockable1& lock1, Lockable2& lock2, LockableN&... lockn);

sizeof...(ParameterPack); // (4)

C++11 標準の 4 つの例はすべて、可変個引数テンプレートを使用しています。最初の 3 つは標準テンプレート ライブラリの一部です。宣言から何を推測できるか見てみましょう。

<オール>
  • std::tuple 任意の数の異なるタイプを受け入れます。
  • std::thread 任意の数の引数で callable を呼び出すことができます。引数にはさまざまなタイプを指定できます。 callable は、関数、関数オブジェクト、ラムダ式など、呼び出すことができるものです。関数 std::thread その呼び出し可能オブジェクトとその引数は、ユニバーサル リファレンスによって取得されます。詳細が必要な場合:テンプレート引数の演繹とユニバーサル参照については、投稿「テンプレート引数」で既に書きました。
  • std::lock アトミックステップで任意の数のロック可能な型をロックできます。アトミック ステップで 1 つのロック可能な型をロックするのは簡単です。したがって、std::lock 少なくとも 2 つの引数が必要です。 Lockable を要件と呼んでいます。 Lockable をサポートする型 メンバ関数 lockが必要です 、 unlock 、および try_lock .
  • sizeof ... - 演算子は ParameterPack の要素数を返します .
  • sizeof... -operator は、コア言語で ParameterPack が使用されているため、特殊なようです。それについて少し書かせてください。

    sizeof.. .-演算子

    sizeof のおかげで ...-operator を使用して、パラメーター パックに含まれる要素の数を直接判断できます。要素は評価されません。

    // printSize.cpp
    
    #include <iostream>
    
    using namespace std::literals;
    
    template <typename ... Args>
    void printSize(Args&& ... args){
     std::cout << sizeof...(Args) << ' '; // (1)
     std::cout << sizeof...(args) << '\n'; // (2)
    }
    
    int main() {
    
     std::cout << '\n';
    
     printSize(); // (3)
     printSize("C string", "C++ string"s, 2011, true); // (4)
    
     std::cout << '\n';
    
    }
    

    sizeof ..-operator を使用すると、コンパイル時にテンプレート パラメーター パック (1) と関数パラメーター パック (2) のサイズを決定できます。空のパラメーター パック (3) と、4 つの要素を含むパラメーター パックに適用します。最初の要素は C 文字列で、2 番目の要素は C++ 文字列です。 C++ 文字列リテラルを使用するには、名前空間 std::literals を含める必要があります (5)。 C++14 は C++ 文字列リテラルをサポートします。

    次は?

    次回の投稿では、可変個引数テンプレートについて深く掘り下げ、可変個引数テンプレートを評価する機能パターンを紹介します。さらに、完全なファクトリ関数を提示し、C++11 から C++17 にジャンプします:C++17 でのフォールド式。