【C++】関数から多値を返す

2021-06-26

C++で関数から複数の値を返したい場合にどうするか。

結論:std::tuple を使うとよい。

#include <tuple>

std::tuple<int, double> func1(int x, double y) {
return {x, y};
}

C言語では戻り値の型は1つしか指定できないので、 複数の値を返したい場合には渡したポインタ先に書き込んでもらうのが常套手段だった。

C++でも1つしか指定できないけど、 std::tuple でまとめて返すことができる。

呼び出し側

呼び出し側では std::get で取り出せる:

#include <iostream>
void test1() {
auto res = func1(1, 2.3);
int x = std::get<0>(res);
double y = std::get<1>(res);
std::cout << x << ", " << y << std::endl;
}

C++14では std::get<型>(tuple) と取り出す型で指定することもできる。

タプル以外にも std::pair や戻り値用の構造体を定義しても実現できる。 pair の場合は要素が2つだけと決まっているのがわかりやすいのと、firstsecondで取り出せるのが便利。 構造体はわざわざ定義する必要があるという手間は増えるが、メンバ名をつけることでわかりやすくなるかもしれない。

余分なコピーは発生しないか?

クラスを自作してコンストラクタ、デストラクタ、代入演算子にログを仕込んで確認したところ、 戻り値最適化(RVO)が効く場合には余分なコピーは発生しておらずムーブされている模様: Wandboxで確認

Foo::ctor()
Foo::move()
Foo::dtor()
Foo, 3
Foo::dtor()

なので安心して使える。

構造化束縛による取り出し

C++17では呼び出し側で構造化束縛(structured bindings)というものが使える:

void test3() {
auto [x, y] = func1(1, 2.3);
std::cout << x << ", " << y << std::endl;
}

コンパイルオプションに -std=c++17 を指定してやる。

構造化束縛を使用しても、さらに余分なムーブが発生するわけではないようなので、安心して使える。