C++0xのtupleを使って関数から複数の戻り値を返す
C++0xのSTLにはtupleテンプレートクラスが導入され、任意の型の任意の要素数の組を必要に応じて定義できるようになった。これを関数の戻り値とすることで複数のオブジェクトを返せないかというお話。ひどくいまさら感のある話題だとも思うし、自分は勉強の仕方がいい加減なので穴もあると思う。ただ、ここ最近、気になってしかたがなかった(使ってみたかった)機能ではあるのでまとめておく。
まず、それができてなにがうれしいか。関数の結果として複数の値を返したいときがままある。たとえば配列の最大要素と最小要素を同時に返すような場合など。この時、従来のC++では、思いつく限り以下の3つのスタイルがとられたと思う。
- 複数のポインタ引数をとる
- 複数の参照引数をとる
- 構造体で返す
で、それぞれ以下の問題がある。
- NULLチェックだるい
- RAIIと相性が悪い
- 構造体定義だるい、関数のバリエーションごとに定義すると思うともっとだるい
で、tupleを返すようにすれば「NULLチェックがいらず、RAIIのスタイルが維持でき、関数の戻り値の記述と構造体の定義が一ヶ所にまとまる」。万事解決。なのか?
とりあえず書こう。
#include <memory> #include <map> #include <utility> #include <tuple> #include <cstdio> #include <cstring> class Test { public: // constructor Test() : mData( new char[100] ) { mData[0] = 0; printf("default constructor\n"); } // copy constructor Test( const Test& test ) : mData( new char[100] ) { memcpy(this->mData, test.mData, 100); printf("copy constructor\n"); } // move constructor Test( Test&& test ) : mData( test.mData ) { test.mData = NULL; printf("move constructor\n"); } // destructor virtual ~Test() throw() { delete[] mData; }; // copy operator Test& operator=(Test& test) { printf("copy operator\n"); memcpy(this->mData, test.mData, 100); return *this; } // move operator Test& operator=(Test&& test) { printf("move operator\n"); std::swap(this->mData, test.mData); return *this; } char* mData; }; std::tuple<Test,Test,Test> func() { using namespace std; return std::tuple<Test, Test, Test>( Test(), Test(), Test() ); } int main(int argc, char* argv[]) { using namespace std; if(true) { // good style tuple<Test, Test, Test> myTuple = func(); Test& test0 = get<0>(myTuple); Test& test1 = get<1>(myTuple); Test& test2 = get<2>(myTuple); } else { // bad style Test test0; Test test1; Test test2; tuple<Test&, Test&, Test&> myTuple(test0, test1, test2); myTuple = func(); } return 0; }
こんな感じになるのかな。こまかいところはご愛嬌。動作確認はGCC4.5.1。
good styleじゃないとRAIIできない上に、無駄なコンストラクタが走る。
good styleの実行結果。
default constructor default constructor default constructor move constructor move constructor move constructor
bad styleの実行結果。
default constructor default constructor default constructor default constructor default constructor default constructor move constructor move constructor move constructor move operator move operator move operator
good styleの不満は2つ。参照で名前をつけなければいけない点と、tupleのどのメンバが何を表すのかがコード上の仕様に現れない点。tuple使う限り仕方がないのかな。
多分なにか見逃してる気がする。