[C++]複数の値を返す関数の書き方考



例として、数値列に対して、最大,最小,平均を計算する関数を考える。


# 戻り値用の型を用意する

関数に対し、戻り値を表す型を定義すれば、
任意の数の値を返す事が出来る。

```cpp
struct Result final
{
  double min     {} ;
  double max     {} ;
  double average {} ;
} ;

Result CalcStatistics( const std::vector<double>& val_ary )
{
  double min     = 0 ;
  double max     = 0 ;
  double average = 0 ;

  ...

  return { min, max, average } ;
}

// 呼び出し(C++17以降)
const auto [min,max,average] = CalcStatistics( val_ary ) ;

// 呼び出し
const auto result = CalcStatistics( val_ary ) ;
const auto& min     = result.min     ;
const auto& max     = result.max     ;
const auto& average = result.average ;
```

型名を単に `Result` としてしまうと、型名が重複してしまうため、
この方法を複数の関数で使う場合は、何らかの対策が必要となる。

## 対策1
関数名に合わせて型名を変える。

```cpp
struct CalcStatisticsResult final
{
  double min     {} ;
  double max     {} ;
  double average {} ;
} ;

CalcStatisticsResult CalcStatistics( const std::vector<double>& val_ary )
{
  double min     = 0 ;
  double max     = 0 ;
  double average = 0 ;

  ...

  return { min, max, average } ;
}

// 呼び出し(C++17以降)
const auto [min,max,average] = CalcStatistics( val_ary ) ;

// 呼び出し
const auto result = CalcStatistics( val_ary ) ;
const auto& min     = result.min     ;
const auto& max     = result.max     ;
const auto& average = result.average ;
```

型推論を使えば型名を書く場面はまず無いので、
名前の長さは気にしなくても良いはず。


## 対策2
関数内にクラスを定義する。

```cpp
auto CalcStatistics( const std::vector<double>& val_ary )
{
  struct Result final
  {
    double min     {} ;
    double max     {} ;
    double average {} ;
  } ;

  double min     = 0 ;
  double max     = 0 ;
  double average = 0 ;

  ...

  return Result{ min, max, average } ;
}

// 呼び出し(C++17以降)
const auto [min,max,average] = CalcStatistics( val_ary ) ;

// 呼び出し
const auto result = CalcStatistics( val_ary ) ;
const auto& min     = result.min     ;
const auto& max     = result.max     ;
const auto& average = result.average ;
```

<!-- ヘッダへ宣言を公開出来ない事に留意。 -->
ヘッダへ宣言を公開する場合には使えないのが欠点。


## 対策3-1
namespace を噛ませる。

```cpp

namespace CalcStatistics {

struct Result final
{
  double min     {} ;
  double max     {} ;
  double average {} ;
} ;

Result Execute( const std::vector<double>& val_ary )
{

  double min     {} ;
  double max     {} ;
  double average {} ;

  ...

  return { min, max, average } ;
}

}

// 呼び出し(C++17以降)
const auto [min,max,average] = CalcStatistics::Execute( val_ary ) ;

// 呼び出し
const auto result = CalcStatistics::Execute( val_ary ) ;
const auto& min     = result.min     ;
const auto& max     = result.max     ;
const auto& average = result.average ;
```

namespace の本来の役割から、ややずれる気はする。

## 対策3-2
namespace の代わりに、クラスとsatatic関数を利用
する。

```cpp

struct CalcStatistics final
{

struct Result final
{
  double min     {} ;
  double max     {} ;
  double average {} ;
} ;

static Result Execute( const std::vector<double>& val_ary )
{

  double min     = 0 ;
  double max     = 0 ;
  double average = 0 ;

  ...

  return { min, max, average } ;
}

} ;

// 呼び出し(C++17以降)
const auto [min,max,average] = CalcStatistics::Execute( val_ary ) ;

// 呼び出し
const auto result = CalcStatistics::Execute( val_ary ) ;
const auto& min     = result.min     ;
const auto& max     = result.max     ;
const auto& average = result.average ;
```

namespace と比べると、少しタイプ数が増える。

# pair,tuple を使う

独自の型を定義する代わりに、標準ライブラリの `pair`,`tuple` を使うという手もある。

 ```cpp
std::tuple<double,double,double> CalcStatistics( const std::vector<double>& val_ary )
{
  double min     = 0 ;
  double max     = 0 ;
  double average = 0 ;

  ...

  return { min, max, average } ;
}

// 呼び出し(C++17以降)
const auto [min,max,average] = CalcStatistics( val_ary ) ;

// 呼び出し
const auto result = CalcStatistics( val_ary ) ;
const auto& min     = get<0>( result ) ;
const auto& max     = get<1>( result ) ;
const auto& average = get<2>( result ) ;
 ```
<!-- 
ただし、値を取り違えるリスクがある。

```cpp
const auto [min,average,max] = CalcStatistics( val_ary ) ; // opps!
``` -->

何番目が何を表すのか、分かりにくいのが欠点。
関数名や戻り値の型で表現しきれない場合は、避けた方が良さそう。 


# 引数を出力に使う

初学者向けの説明で、(何故か)よく見る気がする。

const が付けられ無いのが、余りにも致命的に過ぎる。
個人的には。**特殊な事情が無い限りは非推奨**。

```cpp

void CalcStatistics(
  const std::vector<double>& val_ary,
        double&              min,
        double&              max,
        double&              average
)
{
  ...
}

// 呼び出し
double min     {} ;
double max     {} ;
double average {} ;
CalcStatistics(
  val_ary,
  min, max, average
) ;

```


# note
最大値などの計算は、空配列を渡された場合は行う事が出来ない。
本筋から外れるため考慮しなかったが、本来なら戻り値を [optional](https://cpprefjp.github.io/reference/optional/optional.html) にする方が妥当。

```cpp
struct Result final
{
  double min     {} ;
  double max     {} ;
  double average {} ;
} ;

std::optional<Result> CalcStatistics( const std::vector<double>& val_ary )
{
  if ( val_ary.empty() ) { return std::none ; }

  ...
  return Result{ min, max, average } ;
}
```
* 標準ライブラリは C++17 からだが、boost の物なら C++14 以前でも利用可能。

<!-- 
呼び出し部分の実装作業に関して
マルチカレットを上手く使うと良い
 -->