月別アーカイブ: 2013年5月

C++11 Variadic Templates 可変長引数のテンプレート

C++11 では Template で不定個の引数をまとめて扱えるようになっています。

void func00( int a0, float a1, int a2 )
{
}

template
void Exec00( A0... a0 )
{
   func00( a0... );
}

void VT_Test()
{
   Exec00( 1, 2.0f, 3 );
}

例えば上の例は VT_Test() からそのまま Exec00 経由で func00() を
呼び出しています。
func00() の引数の数が変わっても Tempalte の宣言を変える必要がなくなります。

今までこのようなケースでは、Template にマッチさせるために
引数の数だけオーバーロードか特殊化が必要でした。

template
void Exec01( A0 a0 )
{
   func00( a0 );
}
template
void Exec01( A0 a0, A1 a1 )
{
   func00( a0, a1 );
}
template
void Exec01( A0 a0, A1 a1, A2 a2 )
{
   func00( a0, a1, a2 );
}
~

他にも参照で問題が生じます。
↑ Exec01 はどんな引数でもコンパイルが通りますが、↓ func01() のような
参照を受け取る関数に対して意図しないコピーが渡ってしまいます。

void func01( int& a0 )
{
}

↓ Template 側を参照にすれば解決しますが、今度は定数値(右辺値)を
渡すことができず、Exec02( 100 ) などの呼び出しがエラーになります。

template
void Exec02( A0& a0 )
{
~

定数など右辺値に対しては const の参照も必要となるので、結局
引数の数に加えて参照の組みわせの分だけ宣言が必要となります。

template
void Exec03( A0& a0 )
{
   func00( a0 );
}
template
void Exec03( const A0& a0 )
{
   func00( a0 );
}
template
void Exec03( A0& a0, A1& a1 )
{
   func00( a0, a1 );
}
template
void Exec03( const A0& a0, A1& a1 )
{
   func00( a0, a1 );
}
template
void Exec03( A0& a0, const A1& a1 )
{
   func00( a0, a1 );
}
~

C++ の Rvalue Reference は Move operation だけでなく、
↓参照の転送の問題も解決してくれます。

template
void Exec04( A0&& a0 )
{
   func00( std::forward( a0 ) );
}
~

const 無しに右辺値も左辺値も参照を渡すことができるようになりました。
さらに forward をつけると右辺値参照もそのまま転送されます。

可変引数と合わせると非常に簡単で、これひとつで多くの組み合わせに
対応できることになります。

template
void Exec05( A0&&... a0 )
{
   func00( std::forward( a0 )... );
}

様々なプラットフォームをターゲットにしていると、
コンパイラの対応状況が問題になってきます。

iOS/OSX の Xcode や Android NDK は頻繁に更新されており、
比較的新しい clang/gcc が使えるので問題はないようです。

特に Android NDK の r8e に至っては、 toolchain として
gcc 4.4.3/4.6/4.7, clang 3.1/3.2 の 5種類ものコンパイラが含まれており
選択可能となっています。

VisualStudio 2012 は未対応ですが、November 2012 CTP として
Variadic Templates 対応のコンパイラが公開されています。
正式対応もそう遠くないと考えられます。

NaCl pepper_26 の x86 は gcc 4.4.3 なのでまだコンパイルが通りません。
arm は 4.7.3 が使われているようです。

関連エントリ
スレッド同期命令の比較 C++11 とコンパイラ
C++11 Rvalue Reference 右辺値参照