C++は関数から一つの値しか返せない?
C++ってゅぅのゎ。。
関数から複数の値を返せないの。。。
そして言語仕様も複雑。。。
もぅマヂ無理。。。
Pythonしょ。。。
元ネタ:http://d.hatena.ne.jp/keyword/%A4%E2%A4%A5%A5%DE%A5%C2%CC%B5%CD%FD
C++でも簡単に関数から複数の値を返すことができる!
できるんです!(ただし、C++11以降)
C++11から追加されたstd::tuple
を使います。以下はサンプルコードです。
#include <iostream>
#include <string>
#include <tuple>
std::tuple<int, char, std::string> GreatFunction() {
return std::forward_as_tuple(9, 'q', "qiita");
}
int main() {
int a;
char b;
std::string c;
std::tie(a, b, c) = GreatFunction();
std::cout << a << ", " << b << ", " << c << std::endl;
// 出力 -> 9, q, qiita
return 0;
}
- 戻り値を
std::tuple
にする -
std::forward_as_tuple()
で値を返す -
std::tie()
で受け取る
これで関数から任意の数の戻り値を返すことができます。戻り値が2つの場合はstd::pair
でも同じことができます。
受け取る必要のない引数がある場合はstd::ignore
を使って以下のように書きます。
std::tie(a, std::ignore, c) = GreatFunction();
無駄な処理は無い?
このやり方でオブジェクトを戻り値として返す場合、引数でオブジェクトの参照を渡すやり方(void Function(MyClass &out)
みたいな方法)に比べて無駄な処理が走ります。以下のコードのように自作クラスのオブジェクトを戻り値として返す場合を考えます。
#include <iostream>
#include <tuple>
class A {
public:
A() {
std::cout << "Default constructor" << std::endl;
}
A(const A &a) {
std::cout << "Copy constructor" << std::endl;
}
A(const A &&a) {
std::cout << "Move constructor" << std::endl;
}
A& operator=(const A &a) {
std::cout << "Copy assignment" << std::endl;
return *this;
}
A& operator=(const A &&a) {
std::cout << "Move assignment" << std::endl;
return *this;
}
};
std::tuple<int, A> SuperFunction() {
A a;
return std::forward_as_tuple(1, std::move(a));
}
int main() {
A a;
std::tie(std::ignore, a) = SuperFunction();
return 0;
}
class A
は各コンストラクタ、代入演算子が呼ばれたときに出力するようになっています。これを実行すると、
Default constructor
Default constructor
Move constructor
Move assignment
と出力されます。関数内と引数受け取り側でそれぞれDefault constructorが呼ばれ、return std::forward_as_tuple...
でMove constructorが呼ばれ、std::tie...
で戻り値をa
に代入するときにMove assignmentが呼ばれています。つまり、Default constructor1回分とMove constructor, Move assignmentが無駄な処理として実行されてしまいます。なので、コンストラクタやムーブ代入が重い処理のオブジェクトの場合は注意が必要です。また、ムーブ不可なオブジェクトではコピーが走ってしまうのでこの方法は推奨できません。
そのような場合は、戻り値を以下のコードのようにポインタに変更すれば解決します。
std::tuple<int, std::unique_ptr<A>> ExcellentFunction() {
std::unique_ptr<A> a(new A());
return std::forward_as_tuple(1, std::move(a));
}
int main() {
std::unique_ptr<A> a;
std::tie(std::ignore, a) = ExcellentFunction();
return 0;
}
こうするとA
のコンストラクタが1回呼ばれるだけなので安心して使えます(std::unique_ptr
のコンストラクト、ムーブはありますが、大した処理ではないので問題ないでしょう)。
余談
C++17では複数の値の受け取り方が以下のようにシンプルになるそうです。(P0144R2, P0217R2)
auto [a, b, c] = GreatFunction();
a
,b
,c
の宣言が不要になります。これで無駄なコンストラクタが1つ省けるのかな?
(追記:@yohhoyさんのコメントより)
C++17から関数の戻り値の部分もstd::forward_as_tuple
を使わずに以下のように簡潔に書けるようになるみたいです。(N4387, LWG2367)
std::tuple<int, char, std::string> GreatFunction() {
return {9, 'q', "qiita"};
}
まとめ
-
std::tuple
とstd::tie
を使えば簡単に関数から複数の値を返すことができる - コンストラクト、ムーブが重いオブジェクトやムーブ不可なオブジェクトを返すときは
std::unique_ptr
を使って返す