例として、数値列に対して、最大,最小,平均を計算する関数を考える。
戻り値用の型を用意する
関数に対し、戻り値を表す型を定義すれば、
任意の数の値を返す事が出来る。
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 以前でも利用可能。