読者です 読者をやめる 読者になる 読者になる

c++ で多値を返す

参考1 Returning multiple values from a C++ function - Stack Overflow
参考2 std::tie - cppreference.com

多値を返すには std::tuple を使うと良い。(参考1には pair を使うなどの意見もある)

std::make_tuple で tuple を作る
std::get で要素にアクセスする
std::tie で変数に束縛する

多値を返したくなる例として HSVRGB に変換する(またその逆関数)というのがある。
多値の返せない c なら

void hsv2rgb(double h, double s, double v, double* r, double* g, double* b)
{
    ...
}

と定義して、使うときには

double r,g,b;
hsv2rgb(h,s,v,&r,&g,&b);

と書いていた。

これを tuple を使って書いてみる。ついでに std::get などを試すことにする。

#include <cstdint> // stdint.h を使うことが推奨されているようなので
#include <iostream>
#include <tuple>
#include <cmath>
#include <cassert>

std::tuple<double, double, double> hsv2rgb(double h, double s, double v)
{
    h = fmod(h,360.0); // h は [0,360) の値
    
    assert(0.0<=s && s<=1.0 && 0.0<=v && v<=1.0); // s,v は [0,1] の値

    int64_t H;
    double f, p, q, t;
    double r, g, b;
    
    H = static_cast<int64_t>(h/60);
    f = h/60.0 - static_cast<double>(H);
    p = v*(1.0-s);
    q = v*(1.0-f*s);
    t = v*(1.0-(1.0-f)*s);

    if (s < 1.0e-16) // s == 0.0 でもいいと思うけど
        r = g = b = v;
    else {
        switch (H) {
        case 0:
            r=v; g=t; b=p;
            break;
        case 1:
            r=q; g=v; b=p;
            break;
        case 2:
            r=p; g=v; b=t;
            break;
        case 3:
            r=p; g=q; b=v;
            break;
        case 4:
            r=t; g=p; b=v;
            break;
        case 5:
            r=v; g=p; b=q;
            break;
        default:
            break;
        }
    }
    // make_tuple で tuple を作って返す
    return std::make_tuple(r,g,b);
}

int main()
{
    // コンストラクタで初期化する試し
    std::tuple<int64_t, double, char> tpl(100, 2.718, 'c');
    
    // tuple の要素にアクセスする get<i>(tpl) を試す
    std::cout << std::get<0>(tpl) << ","
              << std::get<1>(tpl) << ","
              << std::get<2>(tpl) << std::endl;

    // hsv2rgb を使ってみる
    // 多値を変数に束縛するには tie を使う
    double r,g,b;
    std::tie(r,g,b) = hsv2rgb(60.0, 0.8, 1.0);
    std::cout << "r = " + std::to_string(r) << std::endl;
    std::cout << "g = " + std::to_string(g) << std::endl;
    std::cout << "b = " + std::to_string(b) << std::endl;

    return 0;
}

そもそも多値の返せる言語なら左辺で受け取るように書きたいと思うがこれでうれしいかどうか。
従来の書き方のほうが余分な宣言がなくてスッキリしているし c の関数をそのまま使える利点がある。

ともかく、いずれ何かに使うかもしれないので記録として残しておく。