9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-05-31

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

戻り値用の型を用意する

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

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

関数名に合わせて型名を変える。

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

関数内にクラスを定義する。

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 を噛ませる。


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関数を利用
する。


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 を使うという手もある。

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 ) ;

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

引数を出力に使う

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

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


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 にする方が妥当。

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 以前でも利用可能。
9
7
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?