ジェネリクス

ジェネリクスtemplate<…>を感覚的に説明してみます。

慣れるまでジェネリクスは難しい。動的言語で実行時に型が決まるほうがわかりやすい。と思います。

(静的型付け言語でジェネリクスはコンパイル時に型が決まります。)

「template<…>ジェネリクス構文はどうやって理解したらいいでしょうか?」

この視点は言語仕様を理解しようとしています。

仕様は目を通して、一通り動かすだけでいいです。

なぜなら、template<…>という言語仕様を理解するのはプログラマではありません。

コンパイラだからです。

プログラマにとってtemplate<…>という宣言はコンパイラの解釈にお付き合いします。という宣言です。

だからtemplate<…>はコンパイルするまで意味を持ちません。そもそも覚えるものではないのです。

リファレンスのコードを見てみましょう。

https://cpprefjp.github.io/lang/cpp11/variadic_templates.html

#include <iostream>
#include <utility>

// パラメータパックが空になったら終了
void print() {}

// ひとつ以上のパラメータを受け取るようにし、
// 可変引数を先頭とそれ以外に分割する
template <class Head, class... Tail>
void print(Head&& head, Tail&&... tail)
{
  std::cout << head << std::endl;

  // パラメータパックtailをさらにheadとtailに分割する
  print(std::forward<Tail>(tail)...);
}

int main()
{
  print(1, 'a', "hello");
}


いや、わかるかい

ここで大事なのはわからないから言語仕様を理解しようとは思わないことです。

必要なのはtemplate<…>をコンパイラはどう解釈するのかということだけです。この説明だけ探しましょう。

(リファレンスにはプログラマからの視点が書いてあります。必要なのはコンパイラからの視点。エラーメッセージが参考になります😂)

コンパイラが最初に受け取っているのはprint関数です。

print(1, 'a', "hello");


これを解釈する方法がtemplate<…>にあります。

template <class Head, class... Tail>
void print(Head&& head, Tail&&... tail)


これでコンパイラはおそらく次のように解釈してくれます。

print(1);
print('a');
print("hello");
print(); //エラーメッセージでこの実装がないって言われて気づいたりします。


ここまでくればあとはわかると思います。

結論、ジェネリクスとはコンパイラの解釈にお付き合いしますという宣言でした。

逆もあります。

プログラマの都合にコンパイラ頑張って付き合ってねという構文もあります。

ラムダ式のキャプチャがそうだと思ってます。


そして実はここからが本題です。


なんでジェネリクスをブログに書いたかといいますと、良い言語、悪い言語っていう評価基準をそれぞれお持ちだと思います。

その評価基準はおそらくプログラマの都合によるものだと思うのです。

プログラマの都合を押し付けれる言語がいいのか、そもそもそんな言語はあるのか。

ないなら不毛な議論なのではと思ってジェネリクスを題材にしました。

あなたの愛する言語はなんですか?(え?日本語?。。。)

ジェネリクスそれは愛の処方箋。

最後までお読みいただきありがとうございました。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です