以下のプログラムで定義されているマップの値を追加しようとしています。
std::map<int, int> floor_plan;
const size_t distance = std::accumulate(std::begin(floor_plan), std::end(floor_plan), 0);
std::cout << "Total: " << distance;
次のエラーが発生します。
エラーC2893:関数テンプレートの特殊化に失敗しました 'unknown-type std :: plus :: operator()(_ Ty1 &&、_ Ty2 &&)const'
std::begin(floor_plan)
は、std::map<int, int>::value_type
であるstd::pair<const int, int>
を指すイテレータを提供します。このペアタイプと整数にはoperator+
が定義されていないため、コードのコンパイルに失敗します。
floor_plan
からマップされたすべての値を合計する場合は、渡された逆参照イテレーターの2番目の要素を抽出できる独自の二項演算子を提供する必要があります。
std::accumulate(std::begin(floor_plan)
, std::end(floor_plan)
, 0
, [] (int value, const std::map<int, int>::value_type& p)
{ return value + p.second; }
);
または、Boost.Iteratorライブラリを利用して、ペアの2番目の要素をその場で抽出することもできます boost::make_transform_iterator
:
#include <boost/iterator/transform_iterator.hpp>
#include <functional>
auto second = std::mem_fn(&std::map<int, int>::value_type::second);
std::accumulate(boost::make_transform_iterator(std::begin(floor_plan), second)
, boost::make_transform_iterator(std::end(floor_plan), second)
, 0);
別のアプローチは、Boost.Rangeライブラリを独自のaccumulate
アルゴリズムの実装とともに使用することです。
#include <boost/range/numeric.hpp>
#include <boost/range/adaptor/map.hpp>
boost::accumulate(floor_plan | boost::adaptors::map_values, 0);
Piotr S.の答えは正しいですが、これが1回限りのタスクでない場合は、そのようなタスクに簡単で便利なファンクターを作成することをお勧めします。
_struct AddValues
{
template<class Value, class Pair>
Value operator()(Value value, const Pair& pair) const
{
return value + pair.second;
}
};
const size_t distance = std::accumulate(plan.begin(), plan.end(), 0, AddValues());
_
テンプレート化されたoperator()
のおかげで、このファンクターをコード内の任意のmap
に使用できます。これは透過コンパレータによく似ていますが、これは透過「加算器」です。
それがどのように機能するかだけでなく、あなたに見せます。
accumulate
以下のような実装の可能性(基本値から合計できるため、init
値があります):
template<class InputIt, class T, class BinaryOperation>
T accumulate(InputIt first, InputIt last, T init,
BinaryOperation op)
{
for (; first != last; ++first) {
init = op(std::move(init), *first); // std::move since C++20
}
return init;
}
したがって、vector
のsum/product
を取得する場合は、次のようになります。
vector<int> vec(5);
std::iota(vec.begin(), vec.end(), 1);
cout<<"vec: ";// output vec
std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(cout, ", "));
// vec: 1, 2, 3, 4, 5,
cout<<"\n vec sum is: "<<accumulate(vec.begin(), vec.end(), 0)<<endl;
// vec sum is: 15
cout<<"vec product is: "<<accumulate(vec.begin(), vec.end(), 1, std::multiplies<int>())<<endl;
// vec product is: 120
std::map
に関しては、map
の2番目の値を合計したいので、マップ内の各second item
を取得する必要があります。したがって、map
でvalue_type
を取得してから、2番目のアイテムを取得する必要があります。 map
のvalue_type
は次のように定義されています。
template <typename Key, typename Value, class Compare = std::less<Key>>
class map{
// using re_tree to sort
typedef Key key_type;
// rb_tree value type
typedef std::pair<key_type, value_type> value_type;
};
たとえば、すべてのsecond/first
値の合計を取得します。
typedef map<string, int> IdPrice;
IdPrice idPrice = {{"001", 100}, {"002", 300}, {"003", 500}};
int sum = accumulate(idPrice.begin(), idPrice.end(), 0, [](int v, const IdPrice::value_type& pair){
return v + pair.second;
// if we want to sum the first item, change it to
// return v + pair.first;
});
cout<<"price sum is: "<<sum<<endl; // price sum is: 900
上記のlambda funtion
のparav
は、初期値0のtmp合計を格納します。