2013年2月3日日曜日

template parameter packとconst T&

variadic templateで次の様な誤ったコードを記述し暫し悩んだ。ポイントはテンプレートパラメーターパックの扱い方被テンプレートパラメーターとする型のconst参照ctorがdeleteである点の2つ。

struct A {
  A(){}
  A(A&&) = delete;
};

struct B {
  template<class T>
  void f(const T& t){ }

  void f(){ }

  template<class T, class ... TS>
  void f(const T& t, TS ... ts){
    f(t);
    f(ts ...);
  }
};

int main(){
  B b;
  A a1, a2, a3;
  b.f(a1, a2, a3);
}

これはvariadic templateを使い慣れて来れば無くなるであろうとても単純な凡ミスで、意図通りに翻訳する為にはB::fの関数仮引数のテンプレートパック側の型宣言をTSではなくconst TS&に変更すれば良い

  template<class T, class ... TS>
  void f(const T& t, const TS& ... ts){
    f(t);
    f(ts ...);
  }

先に挙げた誤った例ではユーザーコードで工夫するなりポインターを使わせるなり或いは黒魔術を使わなければテンプレートパラメーターパックにconst参照を受け取れずに翻訳時に被テンプレート型のconst参照ctorの呼び出しが発生し、A::A(A&&) = deleteされている場合に翻訳時エラーとなる。

仮引数部分にコードしたテンプレートパラメーターパックの仕組みは『{1.内包する要素型} {2.パック演算子} {3.シンボル名}』。1.について内包する要素型ではなくvariadic templateとしてとりあえず全体をひとまとまりに受けるパックされた型と誤解しなければ大丈夫。そうするとvariadic templateの文法や演算子の意味も透き通って理解できる様になる(気がする)。

0 件のコメント:

コメントを投稿