最終更新:2026-10-12
C++ 複数戻り値の書き方¶
今日もまた定番の面接問題をひとつ出しましょう:関数が複数の結果を返すにはどうすればよいか?何通りの方法が思いつきますか?
実はこの問題が問う知識ポイントは非常に多く、次のようなものが関係します:
- ポインタ
- 参照
- lambda
- functional
- tie
- tuple
- struct bingding
- template deduce
- など
本記事ではさまざまな方法でこの問題に答えていきます。まずこの問題を言い換えましょう。a / bを使って模します(以下では被除数が0の場合は考えません)。商と余りを返します。簡単に言えば、1つの関数が商と余りを返す方法は何通りあるか?ということです。
さっそく本題に入ります。注:本節のすべてのコードは星球に先行掲載しています。興味があれば下のQRコードからどうぞ。
1. ポインタと参照¶
インターフェースを変更し、2つの変数を追加して、ポインタと参照を使います。
void divideWithReferences(int dividend, int divisor, int& quotient, int& remainder) {
quotient = dividend / divisor;
remainder = dividend % divisor;
}
void divideWithPointers(int dividend, int divisor, int* quotient, int* remainder) {
if (quotient) *quotient = dividend / divisor;
if (remainder) *remainder = dividend % divisor;
}
この方式の欠点も明らかです。戻り値が2つを超えると、インターフェースが非常に長くなります。さらに、面接官が関数インターフェースの変更を許可しないと言えば、この方法は使えません。
2. Tuple + Tie¶
Tupleとtieの組み合わせは、関数が複数の値を返すための簡潔な方法を提供します。std::tieを使うことで、tupleの要素を別々の変数に展開でき、コードの明瞭さが向上します。
この方法は標準解答のひとつで、比較的シンプルでわかりやすいものです。戻り値が2つならpairでも構いません。
std::tuple<int, int> divide(int dividend, int divisor) {
return std::make_tuple(dividend / divisor, dividend % divisor);
}
std::tie(quotient, remainder) = divide(14, 3);
std::cout << quotient << ", " << remainder << std::endl;
3. Struct Binding¶
上の方法を改造します。C++17で導入された構造化束縛は、複数値返却のコードをさらに簡潔にします。autoと構造化束縛を使うことで、コードはより直感的で理解しやすくなります。
面接官がさらに上の方法を改造するよう求めた場合、次のコード例のようにできます。構造化束縛を使って、コードをよりエレガントにします。
auto divide(int dividend, int divisor) {
struct result {
int quotient;
int remainder;
};
return result{dividend / divisor, dividend % divisor};
}
auto [quotient, remainder] = divide(14, 3);
4. 関数 Callback¶
日常のプロジェクト、特にオープンソースプロジェクトでは、callbackはよく使われる手段です。戻り値を処理するcallbackを渡すことで、ユーザーに処理をカスタマイズさせ、こうして複数の値の返却を実現し、より柔軟なコード構造を実現します。これは非同期プログラミングやイベント処理などのシーンで非常に有用です。
void divide(int dividend, int divisor, std::function<void(int, int)> callback) {
callback(dividend / divisor, dividend % divisor);
}
5. 自作 Out¶
自作outは、構造体でラップする方式で、出力パラメータを構造体のメンバとします。この方式はコードの可読性を高め、特に複数の値を返す必要がある関数に適しています。
template <class T>
struct out {
std::function<void(T)> target;
out(T* t)
: target([t](T&& in) {
if (t) *t = std::move(in);
}) {}
template <class... Args>
void emplace(Args&&... args) {
target(T(std::forward<Args>(args)...));
}
template <class X>
void operator=(X&& x) {
emplace(std::forward<X>(x));
}
template <class... Args>
void operator()(Args&&... args) {
emplace(std::forward<Args>(args)...);
}
};
void divide(int dividend, int divisor, out<int>& quotient_out, out<int>& remainder_out) {
quotient_out.emplace(dividend / divisor);
remainder_out.emplace(dividend % divisor);
}
6. テンプレート推論¶
C++のテンプレート推論は、開発者により柔軟で簡潔なコードの書き方を提供します。テンプレート推論を使えば、型を明示的に指定することなく、異なる型のデータを扱えます。
template <typename T1, typename T2>
struct many {
T1 quotient;
T2 remainder;
};
template <class T1, class T2>
many(T1, T2) -> many<T1, T2>;
many<int, int> divide(int dividend, int divisor) {
return many{
dividend / divisor,
dividend % divisor,
};
}
auto [quotient, remainder] = divide(14, 3);
総じて、C++には複数値返却を実現するさまざまな方法があり、それぞれに適したシーンがあります。適切な方法の選択は具体的な要件次第であり、開発者はコード構造と可読性に応じてこれらの機能を柔軟に使い分けることができます。
関連トピック¶
- C++ 純粋仮想関数と抽象クラス | C++那些事
- C++ assert アサーション | C++那些事
- C++ ビットフィールド | C++那些事
- C 言語による C++ 多態の実現 | C++那些事
- C++ const 定数と修飾 | C++那些事
- C++ const ファミリー | C++那些事
- C++ 間接参照演算子 * | C++那些事
- C++ 初期化と代入 | C++那些事
- C++ mutable キーワード | C++那些事
- C++ restrict とコンパイル最適化 | C++那些事