C・C++には安全な可変長引数がないらしい。
それがなんとC++0xになって初めて搭載されると言うことだ。
ただちょっと腑に落ちないところもある。
まぁとりあえず試してみた。
どうも今C++0xを試すならばGCC 4だということだ。
というわけでCygwin導入。ちゃんとgcc-4ってのがあるのでそれを選らんでインストール。
それでこんなソースコードを書いてみた。
#include <iostream>
int sum(){}
template <typename... Types>
int sum(int x,Types... args){
return x+sum(args...);
}
int main(void){
std::cout << sum(1,2,3) << std::endl;
return 0;
}
さて、これをコンパイルするわけだがとにかくgccには慣れていない僕はgccコマンドでコンパイルしたがエラー。
どうもg++コマンドをつかうべきらしい。
そういえばgccってGnu Compiler Collectionの略なんだよね。
Cのコンパイラはgccコマンド、じゃあC++はg++と。
というわけでこうすればOK。
$ g++-4 -std=c++0x -o test test.cpp
ところがエラー発生。どうもCygwin同梱版の問題らしいが……こうすれば解決。
$ g++-4 -std=c++0x -Wl,--enable-auto-import -o test test.cpp
とりあえずコンパイルしたら ./test で実行。
すると9と表示された。やったね!
これはC++0xの新機能、可変長テンプレート引数によるもの。
可変長テンプレート引数は再帰でのみ解決できるのでこのような方法を取っています。
Typesってのが型の集合。これを引数に入れるときにはTypes…と書く。
それでもって引数の集合はargs、これを引数とするときはargs… と書く。
ここで注意しなければならないのは集合をばらそうとは考えないこと。
再帰で解決することになっているらしい。
まぁしかしながら、型セーフだし、個数もわかるのは大きいよね。
個数はsizeof…(Types)のようにすればわかる。それに再帰の回数をカウントしてもいいよね。
ところで今回はこういうことはできないんかね。
int sum(int x,int... args){
return x+sum(args...);
}
なんでintしか引数に取らないのにわざわざ型引数を取る必要があるんだろう。
型引数の長さで引数の数を指定することになるのかな。
ちなみにint以外のものを引数に入れても、引数を1つずつ減らしていったときに該当する関数が無くなるのでコンパイル時に死ぬ。
なのでC++お得意の意味不明なエラーで困ることになる。
まぁそれは仕方ない。
可変長引数というのはCからある機能ではあるのですが、怖い機能でもありました。
なのでC++ではどちらかというと必要な長さまでいちいち関数を書くということをやっている人が多かった。
かのBoostもそうだ。けどここで初めてできるようになったと。
まぁ扱いは残念ながらいいとは言えませんけどね。だって配列で受けるとかできないわけだし。
けど工夫次第ではなんとでも。
#include <iostream>
#include <vector>
int _sum(const std::vector<int>& list){
int sum;
std::vector<int>::const_iterator end=list.end();
for(std::vector<int>::const_iterator p=list.begin();p!=end;++p){
sum+=*p;
}
return sum;
}
template <typename... Types>
int _sum(std::vector<int>& list,int x,Types... args){
list.push_back(x);
return _sum(list,args...);
}
template <typename... Types>
int sum(Types... args){
std::vector<int> list;
list.reserve(sizeof...(Types));
return _sum(list,args...);
}
std::vectorに再帰的に詰めて、そして最後にstd::vectorだけになったら実際やりたいことをやると。
これも工夫。C++は工夫しないとなかなかうまくいかないけど工夫すればなんとでも。