次の設定でデザインを選択するのに苦労しています。
(C++)関数を作成して(テンプレートコンテナーへの)反復子のペアを取り、反復子が指しているのと同じ型の戻り値を計算しています。
Sum関数を実装したいとしましょう(これはポイントを説明するための単なる例です)。
私の見方では、2つのオプションがあり、どちらにも欠点があります。
オプション1
コンテナーが格納するタイプを定義する関数テンプレートパラメーターT
を作成します。
パラメータは1つだけ必要ですが、MyContainer
のイテレータでのみ関数を使用できます。
template <class T>
T sum(typename MyContainer<T>::IteratorRange range){
T sum;
for (auto it = range.first; it < range.second; ++it) {
sum += *it;
}
return sum;
}
オプション2
関数が任意のIteratorRange
テンプレートクラスと2番目のクラスU
を受け取り、戻り値の型を決定します。
イテレーターは任意のコンテナーから取得できますが、戻り値の型は常にイテレーターが指すものと同じ型ですが、2つのテンプレートクラスが必要です。
template <class IteratorRange, class U>
U sum(IteratorRange range){
U sum;
for (auto it = range.first; it < range.second; ++it) {
sum += *it;
}
return sum;
}
どのオプションがよりクリーンであるか、または両方のオプションの利点を提供する代替手段はありますか?
ボーナス質問
sum
変数をどのように初期化する必要がありますか?
反復する範囲
C++標準ライブラリは、ここでは標準的な形式を提供します accumulate
;
template< class InputIt, class T >
T accumulate( InputIt first, InputIt last, T init );
最初から最後まで(最後を除く)の範囲を取り、範囲内の各要素をinit
値に初期値として追加します。また、戻り値の型を初期型から推測します。
sum
の実装が必要な場合は、accumulate
をそのまま使用することをお勧めします。適切でない場合、範囲[first、last)を取る関数シグネチャは、標準ライブラリをミラーリングするため「通常」です。
戻り値の型を推定します
イテレータの「背後」にある値の型を取得するには、 std::iterator_traits
、特に埋め込み型value_type
に使える;
typedef typename std::iterator_traits<InputIt>::value_type Result;
// .. default initialise
Result sum = Result{}; // or Result() if earlier than C++11
デザインの選択
(オプション2のバリエーション)の機能を設計します。
std::iterator_traits
(ポインタでも機能します)戻り値の型を推測します次のように:
template <class Iterator, class U = typename std::iterator_traits<Iterator>::value_type>
U sum(Iterator first, Iterator last)
{
U sum = U{};
for (auto it = first; it != last; ++it) {
sum += *it;
}
return sum;
}
テンプレート引数を減らした代替案。
template <class Iterator>
typename std::iterator_traits<Iterator>::value_type sum(Iterator first, Iterator last)
{
using U = typename std::iterator_traits<Iterator>::value_type;
U sum = U{};
for (auto it = first; it != last; ++it) {
sum += *it;
}
return sum;
}