C++ および D でのメタプログラミング

D でのテンプレート メタプログラミングに役立つ 2 つの最大の要素は、テンプレートの制約と 03 です。 - C++ が理論的に追加できるものと、C++ に大きな利益をもたらすものの両方。

テンプレート制約を使用すると、テンプレートをインスタンス化できるようにするために真でなければならない条件をテンプレートに設定できます。たとえば、これは 15 のいずれかの署名です のオーバーロード:

R find(alias pred = "a == b", R, E)(R haystack, E needle)
    if (isInputRange!R &&
        is(typeof(binaryFun!pred(haystack.front, needle)) : bool))

このテンプレート化された関数をインスタンス化できるようにするには、型 22 32 で定義された入力範囲でなければなりません (だから 49 57 でなければなりません )、および指定された述語は、指定された引数でコンパイルされ、暗黙的に 62 に変換可能な型を返すバイナリ関数である必要があります .テンプレート制約の条件の結果が 76 の場合 の場合、テンプレートはコンパイルされません。これにより、テンプレートが指定された引数でコンパイルされない場合に C++ で発生する厄介なテンプレート エラーから保護されるだけでなく、テンプレートの制約に基づいてテンプレートをオーバーロードできるようになります。たとえば、 88 の別のオーバーロードがあります

R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
if (isForwardRange!R1 && isForwardRange!R2
        && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)
        && !isRandomAccessRange!R1)

引数はまったく同じですが、制約が異なります。したがって、型が異なれば、同じテンプレート化された関数の異なるオーバーロードと、91 の最適な実装で動作します。 タイプごとに使えます。そのようなことを C++ できれいに行う方法はありません。典型的なテンプレート制約で使用される関数とテンプレートに少し慣れていれば、D のテンプレート制約はかなり読みやすいですが、このようなことを試みるには、C++ で非常に複雑なテンプレート メタプログラミングが必要であり、平均的なプログラマーはそうではありません。実際に自分でやるのはもちろんのこと、理解できるようになります。ブーストはその代表的な例です。いくつかの素晴らしいことを行いますが、信じられないほど複雑です.

108 さらに状況を改善します。テンプレート制約と同様に、コンパイル時に評価できる任意の条件を使用できます。例

static if(isIntegral!T)
{
    //...
}
else static if(isFloatingPoint!T)
{
    //...
}
else static if(isSomeString!T)
{
    //...
}
else static if(isDynamicArray!T)
{
    //...
}
else
{
    //...
}

どのブランチでコンパイルされるかは、最初に 111 と評価される条件によって異なります .したがって、テンプレート内で、テンプレートがインスタンス化された型に基づいて、またはコンパイル時に評価できる他のものに基づいて、実装の一部を特殊化できます。たとえば、128 用途

static if(is(typeof(clock_gettime)))

システムが 139 を提供するかどうかに基づいて、異なる方法でコードをコンパイルする かどうか (144 の場合) ある場合はそれを使用し、そうでない場合は 152 を使用します ).

おそらく、私が見た中で D がテンプレートを改善する最も明白な例は、職場の私のチームが C++ で遭遇した問題です。与えられた型が特定の基本クラスから派生したものかどうかに基づいて、テンプレートを異なる方法でインスタンス化する必要がありました。このスタックオーバーフローの質問に基づいたソリューションを使用することになりました。動作しますが、ある型が別の型から派生しているかどうかをテストするだけではかなり複雑です。

ただし、D では、161 を使用するだけです。 オペレーター。例

auto func(T : U)(T val) {...}

170 の場合 184 に暗黙的に変換可能 (193 の場合と同様) 207 から派生しました )、次に 212 226 の場合はコンパイルされます 238 に暗黙的に変換できません 、それはしません。 それ 簡単な改善により、基本的なテンプレートの特殊化でさえもはるかに強力になります (テンプレートの制約や 247 がなくても) ).

個人的には、コンテナーと 259 の時折の関数を除いて、C++ でテンプレートを使用することはめったにありません。 、使用するのがとても面倒だからです。それらは醜いエラーを引き起こし、空想的なことを行うのは非常に困難です。少しでも複雑なことを行うには、テンプレートとテンプレートのメタプログラミングに非常に熟練している必要があります。しかし、D のテンプレートはとても簡単なので、私はいつもそれらを使用しています。エラーは理解と対処がはるかに簡単です (ただし、テンプレート化されていない関数で通常発生するエラーよりもさらに悪いですが)、派手なメタプログラミングで言語を強制的に実行させる方法を理解する必要はありません。 .

C++ が D が持っているこれらの機能の多くを得ることができなかった理由はありません (C++ の概念は、それらを整理することができれば役立ちます) が、テンプレート制約と 260 C++ に移行すると、C++ テンプレートは使いやすさとパワーの点で D テンプレートと比較できなくなります。


何年も前に見つけたこのレンダラーほど、D テンプレート システムの驚異的なパワー (TM) を示すのに適したものはないと思います:

はい!これは実際に コンパイラ によって生成されたものです ... それは「プログラム」であり、実にカラフルなものです。

編集

ソースがオンラインに戻ったようです。


D メタプログラミングの最良の例は、C++ Boost および STL モジュールに対して、それを多用する D 標準ライブラリ モジュールです。 D の std.range、std.algorithm、std.functional、std.parallelism を確認してください。これらのどれも、少なくとも D モジュールが持つクリーンで表現力豊かな API を使用して、C++ で簡単に実装できるものではありません。

D メタプログラミングを学ぶ最良の方法は、私見ですが、これらの種類の例を使用することです。 Andrei Alexandrescu (C++ テンプレート メタプログラミングの第一人者で、D に深く関わっている) によって書かれた std.algorithm と std.range のコードを読むことで、主に学びました。その後、学んだことを利用して std.parallelism モジュールに貢献しました。

また、D には C++1x の 272 に似たコンパイル時関数評価 (CTFE) があることに注意してください。 しかし、実行時に評価できる大規模で成長している関数のサブセットは、コンパイル時に変更せずに評価できるという点で、はるかに一般的です。これはコンパイル時のコード生成に役立ち、生成されたコードは文字列 mixin を使用してコンパイルできます。