c++的缺点是什么,就是元编程太麻烦.
c++必须要有根本性的改变,才能让元编程更方便.虽然c++20已经不错了.
d自定义打印格式
传统:

struct S
{
    ... // my sooper sekret data here!
    string toString() const pure @safe
    {
        // 最小化构造串
        auto app = appender!string();
        ... //数据转为串
        return app.data;
    }                                                                                        
}

void main()
{
    auto s = S();
    ... //处理s
    writeln(s);
}

变化

struct S
{
    void toString(scope void delegate(const(char)[]) sink,
                  FormatSpec!char fmt) const
    {//其实就是加几个模板参数.
        switch(fmt.spec)
        {
            // Look, ma! I invented my own format specs!
            case 'i':
                // output first format to sink
                break;
            case 'j':
                // output second format to sink
                break;
            case 's':
                // output boring default string format
                break;
            default:
                throw new Exception("Unknown format specifier: %" ~                                fmt.spec);
        }
    }
}

注意:使用FormatSpec!char作为格式化为UTF-8字符串的最常见情况。如果您还希望以16位wstring或32位dstring实现格式化,则必须实现以FormatSpec!wchar或FormatSpec!dchar作为参数的toString函数

更好的C:

import core.stdc.stdio;

extern(C):

int main()
{
    printf("1 + 1 = %d!\n", 1 + 1);
    return 0;
}

$ dmd -betterC example.d
$ ./example
1 + 1 = 2!

将枚举类型的名称作为数组:

enum State
{
    stopped,
    starting,
    running,
    stopping,
}

string[] state_names = [__traits(allMembers, State)];

一篇文章对比c++与d的元编程:在此
什么是编译时查找.编译时查找已知的串,就是这个串,我编译时就已经知道了,类似手写串.而不是运行时才知道.因而这时根本就没必要再进行运行时查找了.就相当于串作为模板的参数,因为串已知.所以模板<串>就是个固定已知的函数了,没必要再查找这个函数了.直接调用即可.
直接看d语言的实现.c++如果不进行一番大的改造,是不能优雅实现的,借助于hana,但仍不是很漂亮.因为反射及很多还未实现.
D的元编程为什么厉害,就是把一切符号当作占位符.最后填占位符就是了.其实现代的宏编程也是这个玩意.最后都是AST(语法树).

void main() {
  EventSystem!("foo", "bar", "baz") events;//编译时字符串与常规串一样.

  events.on!"foo"(() { writeln("foo triggered!"); });
  events.on!"foo"(() { writeln("foo again!"); });
  events.on!"bar"(() { writeln("bar triggered!"); });
  events.on!"baz"(() { writeln("baz triggered!"); });
  // events.on!"unknown"(() {}); // compile error!

  events.trigger!"foo";
  events.trigger!"baz";
  events.trigger("bar"); // overload for dynamic dispatch
  // events.trigger!"unknown"; // compile error!
}
//接着
 void on(string event)(Callback c) {
    enum index = staticIndexOf!(event, events);//枚是关键
    static assert(index >= 0,
      "trying to add a callback to an unknown event: " ~ event);

    callbacks_[index] ~= c;
  }

  void trigger(string event)() {
    enum index = staticIndexOf!(event, events);//枚
    static assert(index >= 0,
      "trying to trigger an unknown event: " ~ event);

    foreach (callback; callbacks_[index])
      callback();
  }

d有两个编译时阶段:一,ast树,二,ctfe.前面有文章.

void trigger(string event) {
    foreach (i, e; events) {
      if (event == e) {
        foreach (c; callbacks_[i])
          c();
        return;
      }
    }
    assert(false, "trying to trigger an unknown event: " ~ event);
  }  
 struct Entity(arg...)
  if (arg.length == 1)
{
  static if (is(arg[0])) {
    alias Type = arg[0];
  } else static if (is(typeof(arg[0]) T)) {
    alias Type = T;
    enum value = arg[0];
  }
}
这样来用Entity!"foo"()来创建对象Entity!double(),简化:
enum c(arg...) = Entity!arg();

static assert(is(c!int.Type == int));
static assert(is(c!"foo".Type == string));
static assert(c!"foo".value == "foo");
static assert(c!42.value == 42);

下面:
我们需要一种可以存储不同类型的值的东西(例如std.typecons.Tuple,其中键是类型或编译时值,其实例化如下:

struct Bar {}
Map!(//存储不同类型的键与值.
  "foo",  int,           // string "foo" maps to a value of type int
  Bar,    string,        // type Bar maps to a value of type string
  "한",   string[]) map; // string "한" maps to a value of type string[]
我们需要将交织键和值类型分开,并声明值本身的存储:

struct Map(spec...) {//这就是个把整个类型序化,然后对这个类型序列求值.
  alias Keys = Even!spec;//偶数项,编译时求类型值
  alias Values = Odd!spec;//所有奇数项类型.
  Values values;
实现见下:

template Stride(size_t first, size_t stride, A...) {
  static if (A.length > first)
    alias Stride = AliasSeq!(A[first], Stride!(stride, stride, A[first .. $]));//这里,又是个静态递归.stride为步数.估计就是递归取第几项,相当求所有余3等等的序列.只不过是静态编译时求值.
  else
    alias Stride = AliasSeq!();//别名序列.序列这个玩意儿还是不错
}

alias Odd(A...) = Stride!(1, 2, A);//这个其实就是个索引,2%1的
alias Even(A...) = Stride!(0, 2, A);//第2项的第0项,2%0的项.与我前面

使用:
  // 初化
  auto map =
    Map!("foo", int, Bar, string, "한", string[])(
      42, "baz", ["lorem", "ipsum", "dolor"]);

  //迭代键
  foreach (K; map.Keys)
    writeln(K.stringof);

  //迭代值类型
  foreach (V; map.Values)
    writeln(V.stringof);

  // 迭代值
  foreach (value; map.values)
    writeln(value);

重载in.最后了,坚持下去.

static bool opBinaryRight(string op, Key...)(Entity!Key key)
    if (op == "in")
  {//静仅取决于编译时,二分右,
    enum index = staticIndexOf!(Key, Keys);//即某个类型是否在类型序列中.
    return index >= 0;
  }
   static assert(c!"foo" in map);
  static assert(c!Bar in map);
  static assert(c!"한" in map);
  static assert(c!42 !in map);
   private template IndexOf(alias Key) {
    enum IndexOf = staticIndexOf!(Key, Keys);
    static assert(IndexOf >= 0,
      "trying to access a nonexistent key: " ~ Key);
  }//包装.
   auto opIndex(Key...)(Entity!Key key) const {
    return values[IndexOf!Key];
  }

  auto opIndexAssign(T, Key...)(auto ref T value, Entity!Key key) {
    return values[IndexOf!Key] = value;
  }

  auto opIndexOpAssign(string op, T, Key...)(auto ref T value, Entity!Key key) {
    return mixin(`values[IndexOf!Key] ` ~ op ~ `= value`);
  }//映射的键索引.类似 映[键]
测试:
 //编译时查找键的类型/值,运行时比较
  map[c!"foo"] = 42;        // opIndexAssign
  map[c!Bar] = "baz";
  map[c!"한"] ~= "lorem";    // opIndexOpAssign!"~"
  map[c!"한"] ~= "ipsum";
  map[c!"한"] ~= "dolor";

//同样
  assert(map[c!"foo"] == 42);  // opIndex
  assert(map[c!Bar] == "baz");
  assert(map[c!"한"] == ["lorem", "ipsum", "dolor"]);