Behavior with std::erase() non-references vs reference - c++

Suppose I have a vector of type my_object which has a size of 3 and I want to get 3 elements from my vector storing them in a reference
Then I want to remove and erase element_3 by using std::remove_if() and element_1 and element_2 by using std::remove
Here is my_object:
class my_object {
public:
my_object(int num);
bool exists() const;
private:
int num;
};
my_object::my_object(int num) : num(num) {}
bool my_object::exists() { return num == 1; }
Here is main:
std::vector<my_object> my_vector;
int main() {
my_object e1(2);
my_object e2(2);
my_object e3(1); // i.e exists() will return true in lambda
my_vector.push_back(e1);
my_vector.push_back(e2);
my_vector.push_back(e3);
const auto& element_1 = my_vector.at(0);
const auto& element_2 = my_vector.at(1);
const auto& element_3 = my_vector.at(2);
auto lambda = [](auto& src) { return src.exists() };
std::erase(std::remove_if(b, e, lambda), e); // remove_if for element_3
std::erase(std::remove(b, e, element_1), e);
std::erase(std::remove(b, e, element_2), e);
return 0;
}
What is extremely weird is that when I declare element_1, element_2, element_3 by reference than the erasing isn't done properly and the size isn't decreased to 0, but when I write const auto with no & then it works perfectly fine, can anyone explain this weird behavior to me?

Discounting the methods of erasure, those references are just that: references to objects living in the container. Once remove or remove_if have performed their tasked move-assignment while marching up the sequence, those references are still referring to the same elements, but the occupants of those slots are:
At best, still valid objects because a valid object either stayed where it was, or one was move-assigned there.
Mere shells of some former self because the reference now refers to a source object that was never reclaimed by a targeted move.
I'm not going to dive into std::remove, Rather. Look at this rather trivial example of std::remove_if
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> v = { 1,2,3,4,5 };
const auto& a1 = v.at(0);
const auto& a2 = v.at(2);
const auto& a3 = v.at(4);
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
std::remove_if(v.begin(), v.end(), [](const auto& x) { return x == 3; });
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
}
Output
1 3 5
1 4 5
As you can see, the functional description of std::remove_if lives up to what you see in the code. The 3 element was removed, and the 4 element was move-assigned to its place. What you don't see here is that the 5 element was move-assigned to the 4's place, and the 5 value you see here now happens to be coming from the slot where 5 was. The standard says that object is "valid", but with an "unspecified" value. We can verify that by ensuring our move-source of a move-assignment is, in fact, "invalid" (as far as we're concerned). Modifying our original program gives us this:
#include <iostream>
#include <vector>
#include <algorithm>
struct S
{
S(int n) : value(n), isvalid(true)
{
}
S(const S& s) : value(s.value), isvalid(true)
{
}
S(S&& s) : value(s.value), isvalid(true)
{
s.isvalid = false;
}
S& operator =(S&& s)
{
value = s.value;
isvalid = s.isvalid;
s.isvalid = false;
return *this;
}
int value;
bool isvalid;
};
std::ostream& operator <<(std::ostream& outp, const S& s)
{
outp << s.value << '(' << std::boolalpha << s.isvalid << ')';
return outp;
}
int main()
{
std::vector<S> v = { 1,2,3,4,5 };
const auto& a1 = v.at(0);
const auto& a2 = v.at(2);
const auto& a3 = v.at(4);
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
std::remove_if(v.begin(), v.end(), [](const auto& x) { return x.value == 3; });
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
}
Output
1(true) 3(true) 5(true)
1(true) 4(true) 5(false)
The bottom line: your references are still referring to the same slots they were before, but the elements have been either (a) move assigned to something else, or (a) no longer containing specified content. Tread carefully when using references to container contents when performing container modifications.
I reserve comment on the std::erase calls, as I have no idea what you're doing there at all. To my knowledge that isn't even a function in the standard library (wouldn't be the first time I missed out on a new function, but scraping over cppreference yields nothing, so take that for what its worth).

Related

Is there a cost difference between returning a function by reference vs passing the object through a void function?

When would you choose to prefer one way over the other? When I ran the bottom example through Godbolt. The return by reference function calls a move at the very end. If the container being passed in was large enough would that be costly? Or would it simply move the pointers around?
Ex:
#include <iostream>
#include <vector>
template<typename T>
void print(const T& v)
{
for(const auto& e : v)
std::cout << e << " ";
std::cout << std::endl;
}
void byVoid(std::vector<int>& v)
{
for(auto && e : v)
e = 0;
}
std::vector<int>& byRef(std::vector<int>& v)
{
for(auto && e : v)
e = 0;
return v;
}
int main()
{
std::vector<int> test1 = {0,1,2,3,4,5,6,7,8,9};
std::vector<int> test2 = {0,1,2,3,4,5,6,7,8,9};
print(test1);
print(test2);
byVoid(test1);
test2 = byRef(test2);
print(test1);
print(test2);
}

C++ - Merging two sorted vectors of different, unique types with a common attribute into a new sorted vector

There are two types, A and B. These types both have a common attribute, a key.
There are two vectors of type A and B. These vectors are sorted by key ascending. The vectors are unique in regards to their keys - ie if a key is present in A_vect it is guaranteed to not be present in B_vect and vice versa.
The goal is to take the keys in vector A_vect, construct new B types and put them into B_vect, such that B contains it's original keys and it's new keys. The new B_vect should also be sorted.
I have this code snippet so far:
#include <iostream>
#include <vector>
#include <algorithm>
struct A
{
A(int i)
{
key = i;
}
int key;
};
struct B
{
B(int i)
{
key = i;
}
B()
{
}
int key;
};
int main()
{
std::vector<A> a_diff_sorted_vect = {1, 3};
std::vector<B> b_sorted_vect = {2, 4};
std::transform
(
a_diff_sorted_vect.begin(), a_diff_sorted_vect.end(),
std::back_inserter(b_sorted_vect),
[](const A &a) -> B
{
B b;
b.key = a.key;
return b;
}
);
std::cout << "Printing b_sorted_vect" << "\n";
for(auto element : b_sorted_vect)
{
std::cout << std::to_string(element.key) << "\n";
}
std::cout << "Finished printing b_sorted_vect" << "\n";
/*
std::sort
(
b_sorted_vect.begin(),
b_sorted_vect.end(),
[](B lhs, B rhs)
{
return lhs.key < rhs.key;
}
);
std::cout << "Printing b_sorted_vect" << "\n";
for(auto element : b_sorted_vect)
{
std::cout << std::to_string(element.key) << "\n";
}
std::cout << "Finished printing b_sorted_vect" << "\n";
*/
return 0;
}
Output:
2
4
1
3
Desired output:
1
2
3
4
Ideally I would like to avoid the top-down std::sort at the end, as I think there might be a more efficient way to do that during construction and merging. The std::sort is my current solution.
The use of a third temporary vector might be necessary, which is fine.
This should be done with C++11, using boost if necessary.

C++ - Iterate over vector of doubles as tuples

I have a C++ vector of doubles, which is guaranteed to have an even number of elements. This vector stores the coordinates of a set of points as x, y coordinates:
A[2 * i ] is the x coordinate of the i'th point.
A[2 * i + 1] is the y coordinate of the i'th point.
How to implement an iterator that allows me to use STL style algorithms (one that takes an iterator range, where dereferencing an iterator gives back the pair of doubles corresponding to the x, y coordinates of the corresponding point) ?
I'm using C++17 if that helps.
We can use range-v3, with two adapters:
chunk(n) takes a range and adapts it into a range of non-overlapping ranges of size n
transform(f) takes a range of x and adapts it into a range of f(x)
Putting those together we can create an adaptor which takes a range and yields a range of non-overlapping pairs:
auto into_pairs = rv::chunk(2)
| rv::transform([](auto&& r){ return std::pair(r[0], r[1]); });
Using the r[0] syntax assumes that the input range is random-access, which in this case is fine since we know we want to use it on a vector, but it can also be generalized to work for forward-only ranges at the cost of a bit more syntax:
| rv::transform([](auto&& r){
auto it = ranges::begin(r);
auto next = ranges::next(it);
return std::pair(*it, *next);
})
Demo, using fmt for convenient printing:
int main() {
std::vector<int> v = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5};
auto into_pairs = rv::chunk(2)
| rv::transform([](auto&& r){ return std::pair(r[0], r[1]); });
// prints {(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)}
fmt::print("{}\n", v | into_pairs);
}
It's unclear from the question if you wanted pair<T, T>s or pair<T&, T&>s. The latter is doable by providing explicit types to std::pair rather than relying on class template argument deduction.
C++ is a bit of a moving target - also when it comes to iterators (c++20 has concepts for that...). But would it not be nice to have a lazy solution to the problem. I.e. The tuples get generated on the fly, without casting (see other answers) and without having to write a loop to transform a vector<double> to a vector<tuple<double,double>>?
Now I feel I need a disclaimer because I am not sure this is entirely correct (language lawyers will hopefully point out, if I missed something). But it compiles and produces the expected output. That is something, yes?! Yes.
The idea is to build a pseudo container (which actually is just a facade to an underlying container) with an iterator of its own, producing the desired output type on the fly.
#include <vector>
#include <tuple>
#include <iostream>
#include <iterator>
template <class SourceIter>
struct PairWise {
PairWise() = delete;
PairWise(SourceIter first, SourceIter last)
: first{first}
, last{last}
{
}
using value_type =
typename std::tuple<
typename SourceIter::value_type,
typename SourceIter::value_type
>;
using source_iter = SourceIter;
struct IterState {
PairWise::source_iter first;
PairWise::source_iter last;
PairWise::source_iter current;
IterState(PairWise::source_iter first, PairWise::source_iter last)
: first{first}
, last{last}
, current{first}
{
}
friend bool operator==(const IterState& a, const IterState& b) {
// std::cout << "operator==(a,b)" << std::endl;
return (a.first == b.first)
&& (a.last == b.last)
&& (a.current == b.current);
}
IterState& operator++() {
// std::cout << "operator++()" << std::endl;
if (std::distance(current,last) >= 2) {
current++;
current++;
}
return *this;
}
const PairWise::value_type operator*() const {
// std::cout << "operator*()" << std::endl;
return std::make_tuple(*current, *(current+1));
}
};
using iterator = IterState;
using const_iterator = const IterState;
const_iterator cbegin() const {
return IterState{first,last};
}
const_iterator cend() const {
auto i = IterState{first,last};
i.current = last;
return i;
}
const_iterator begin() const {
// std::cout << "begin()" << std::endl;
return IterState{first,last};
}
const_iterator end() const {
// std::cout << "end()" << std::endl;
auto i = IterState{first,last};
i.current = last;
return i;
}
source_iter first;
source_iter last;
};
std::ostream& operator<<(std::ostream& os, const std::tuple<double,double>& value) {
auto [a,b] = value;
os << "<" << a << "," << b << ">";
return os;
}
template <class Container>
auto pairwise( const Container& container)
-> PairWise<typename Container::const_iterator>
{
return PairWise(container.cbegin(), container.cend());
}
int main( int argc, const char* argv[]) {
using VecF64_t = std::vector<double>;
VecF64_t data{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
for (const auto x : pairwise(data)) {
std::cout << x << std::endl;
}
return 0;
}
Elements in vector are stored in contiguous memory area, so you can use simple pointer arithmetics to access pair of doubles.
operator++ for iterator should skip 2 doubles at every use.
operator* can return tuple of references to double values, so you can read (pair = *it) or edit values (*it = pair).
struct Cont {
std::vector<double>& v;
Cont(std::vector<double>& v) : v(v) {}
struct Iterator : public std::iterator<std::input_iterator_tag , std::pair<double,double>> {
double* ptrData = nullptr;
Iterator(double* data) : ptrData(data) {}
Iterator& operator++() { ptrData += 2; return *this; }
Iterator operator++(int) { Iterator copy(*this); ptrData += 2; return copy; }
auto operator*() { return std::tie(*ptrData,*(ptrData+1)); }
bool operator!=(const Iterator& other) const { return ptrData != other.ptrData; }
};
auto begin() { return Iterator(v.data()); }
auto end() { return Iterator(v.data()+v.size());}
};
int main() {
std::vector<double> v;
v.resize(4);
Cont c(v);
for (auto it = c.begin(); it != c.end(); it++) {
*it = std::tuple<double,double>(20,30);
}
std::cout << v[0] << std::endl; // 20
std::cout << v[1] << std::endl; // 30
}
Demo
There's not an easy "C++" way to do this that's clean and avoids a copy of the original array. There's always this (make a copy):
vector<double> A; // your original list of points
vector<pair<double,double>> points;
for (size_t i = 0; i < A.size()/2; i+= 2)
{
points[i*2] = pair<double,double>(A[i], A[i+1]);
}
The following would likely work, violates a few standards, and the language lawyers will sue me in court for suggesting it. But if we can assume that the sizeof(XY) is the size of two doubles, has no padding, and expected alignment then cheating with a cast will likely work. This assumes you don't need a std::pair
Non standard stuff ahead
vector<double> A; // your original list of points
struct XY {
double x;
double y;
};
static_assert(sizeof(double)*2 == sizeof(XY));
static_assert(alignof(double) == alignof(XY));
XY* points = reinterpret_cast<XY*>(A.data());
size_t numPoints = A.size()/2;
// iterate
for (size_t i = 0; i < numPoints; i++) {
XY& point = points[i];
cout << point.x << "," << point.y << endl;
}
If you can guarantee that std::vector will always have an even number of entries, you could exploit the fact that an vector of doubles will have the same memory layout as a vector of pairs of doubles. This is kind of a dirty trick though so I wouldn't recommend it if you can avoid it. The good news is that the standard guarantees that vector elements will be contiguous.
inline const std::vector<std::pair<double, double>>& make_dbl_pair(std::vector<double>& v)
{
return reinterpret_cast<std::vector<std::pair<double, double>>&>(v);
}
This will only work for iteration with iterators. The size is likely to be double the number of pairs in the vector because it is still a vector of doubles underneath.
Example:
int main(int argc, char* argv[])
{
std::vector<double> dbl_vec = { 0.0, 1.1, 2.2, 3.3, 4.4, 5.5 };
const std::vector<std::pair<double, double>>& pair_vec = make_dbl_pair(dbl_vec);
for (auto it = pair_vec.begin(); it != pair_vec.end(); ++it) {
std::cout << it->first << ", " << it->second << "\n";
}
std::cout << "Size: " << dbl_vec.size() << "\n";
return 0;
}

How to encapsulate custom iterator in function using boost-range

Lately I was using boost-range to create ranges over elements satisfying certain criteria. In all cases I'm using the same kind of filtered range all the time, so that I tried to encapsulate this behaviour in an external function.
This was the point where my problems started. Consider the following example.
#include <boost/range/adaptor/filtered.hpp>
#include <iostream>
#include <vector>
auto myFilter = [](const std::vector<int>& v, int r) {
return v | boost::adaptors::filtered([&r](auto v) { return v%r == 0; });
};
int main(int argc, const char* argv[])
{
using namespace boost::adaptors;
std::vector<int> input{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (auto& element : input | filtered([](auto v) {return v % 2 == 0; } ))
{
std::cout << "Element = " << element << std::endl;
}
std::cout << std::endl;
for (auto& element : myFilter(input,4))
{
std::cout << "Element = " << element << std::endl;
}
return 0;
}
The first for-loop behaves as expected printing 4 and 8. The second for-loop however prints just 4. Why is that?
My second idea was to implement a class having a begin() and end() function. This should be a thin wrapper around a range object.
This was the solution, after fiddling out the type of the range iterator.
struct MyFilter {
MyFilter(const std::vector<int>& c, int r) : c(c), r(r), f([&r](auto v) { return v%r == 0; }) {
}
boost::range_detail::filtered_range<std::function<bool(int)>, std::vector<int>>::iterator begin() {
return rng.begin();
}
boost::range_detail::filtered_range<std::function<bool(int)>, std::vector<int>>::iterator end() {
return rng.end();
}
std::vector<int> c;
int r;
std::function<bool(int)> f;
boost::range_detail::filtered_range < std::function<bool(int)>, std::vector<int>> rng=c | boost::adaptors::filtered(f);
};
Usage should be something like:
for (auto& element : MyFilter(input, 4)) {
std::cout << "Element = " << element << std::endl;
}
Unfortunately, it prints again just the 4. Whichs is quite strange to me??
Now, I got the solution by myself. I have to remove the "&" in my lambda function to make it work!
In:
auto myFilter = [](const std::vector<int>& v, int r) {
return v | boost::adaptors::filtered([&r](auto v) { return v%r == 0; });
};
It returns another range adaptor while r captured by reference becomes a dangling reference. To fix it capture r by value:
auto myFilter = [](const std::vector<int>& v, int r) {
return v | boost::adaptors::filtered([r](auto v) { return v%r == 0; });
}; ^
+--- capture by value

How to pass fields from a class to function in c++?

In some words: how can I pass various fields from a custom class to a single function?
Now in details:
I have a std::vector containing a class, for example CustomClass from which I have to extract a result from a field from this class by some criteria which are fields in this class and to combine somehow this data.
My first approach to this problem was to use a function which accepts as a parameter the std::vector of the class in order to extract the data and return a std:map. The key in this map is the type of the criteria by which the data should be combined and the value is an int with the combined data from all members of this vector.
The problem is that the criteria is not only one - more than one field from this class may be used as criteria (let for easiness all of the criteria are std::string, if they are not - I could make the function templated).
The easiest way for me now is to make dozens of functions with almost identical code and each of them to extract a simple concrete field from this class. However changes might require similar changes to all of the dozens of functions which would be a maintenance headache. But in this stage I cannot think how to pass to a single function a field from this class...
Here's an example code from this class:
// this is the class with data and criteria
class CustomClass
{
public:
std::string criteria1;
std::string criteria2;
std::string criteria3;
//... and others criteria
int dataToBeCombined;
// other code
};
// this is one of these functions
std::map<std::string, int> getDataByCriteria1(std::vector<CustomClass> aVector)
{
std::map<std::string, int> result;
foreach(CustomClass anObject in aVector)
{
if(result.find(anObject.criteria1)==result.end()) // if such of key doesn't exists
{
result.insert(std::make_pair(anObject.criteria1, anObject.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
}
}
return result;
}
and by similar way I should make the other functions which should work with CustomClass::criteria2, CustomClass::criteria3, etc.
I thought to make these criteria in a single array and to pass to this function only the number of the criteria but the class will be used by others for other purposes and the fields must be easy to read, so this will not be an option (i.e. the real names are not criteria1, criteria2, etc. but are descriptive).
Anyone with ideas?
EDIT: Someone referred my question to "C++ same function parameters with different return type" which obviously is very different - the function in my case return the same type every time, just the parameters it takes must be various fields from a class.
You can use pointer to member. Declare an argument std::string CustomClass::*pField in your function, pass it with &CustomClass::criteriaN, access it with anObject.*pField.
See more on the topic: Pointers to data members.
If all "criteria" are of the same type, I don't see an elegant solution but you can "enumerate" they in some way and use their number.
By example, you can declare a templated getVal() method in CustomClass in this way
template <int I>
const std::string & getVal () const;
and implement they, number by number, criteria by criteria, in this way (outside the body of the class)
template <>
const std::string & CustomClass::getVal<1> () const
{ return criteria1; }
template <>
const std::string & CustomClass::getVal<2> () const
{ return criteria2; }
template <>
const std::string & CustomClass::getVal<3> () const
{ return criteria3; }
Now, you can transform getDataByCriteria1() in a templated function getDataByCriteria() in this way
template <int I>
std::map<std::string, int> getDataByCriteria (std::vector<CustomClass> aVector)
{
std::map<std::string, int> result;
for (const auto & cc : aVector)
{
if ( result.find(cc.getVal<I>()) == result.end()) // if such of key doesn't exists
{
result.insert(std::make_pair(cc.getVal<I>(), cc.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
}
}
return result;
}
and call it in this way
auto map1 = getDataByCriteria<1>(ccVec);
auto map2 = getDataByCriteria<2>(ccVec);
auto map3 = getDataByCriteria<3>(ccVec);
--- EDIT: added solution (C++14 only) for different types criteria ---
A little different if the "criteria" are of different types.
The solution work but in C++14, thanks to auto and decltype().
By example, if
std::string criteria1;
int criteria2;
long criteria3;
You can declare getVal() with auto
template <int I>
const auto & getVal () const;
and define (with auto) all versions of getVal()
template <>
const auto & CustomClass::getVal<1> () const
{ return criteria1; }
template <>
const auto & CustomClass::getVal<2> () const
{ return criteria2; }
template <>
const auto & CustomClass::getVal<3> () const
{ return criteria3; }
and combining auto with decltype(), you can modify getDataByCriteria() in this way
template <int I>
auto getDataByCriteria (std::vector<CustomClass> aVector)
{
std::map<decltype(aVector[0].getVal<I>()), int> result;
for (const auto & cc : aVector)
{
if ( result.find(cc.getVal<I>()) == result.end()) // if such of key doesn't exists
{
result.insert(std::make_pair(cc.getVal<I>(), cc.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
}
}
return result;
}
The use of the function remain the same (thanks to auto again)
auto map1 = getDataByCriteria<1>(ccVec);
auto map2 = getDataByCriteria<2>(ccVec);
auto map3 = getDataByCriteria<3>(ccVec);
p.s.: caution: code not tested
p.s.2 : sorry for my bad English
You can use a function to extract a filed such as
std::string extractFiled(const CustomClass &object, int which) {
switch (which) {
case 1:
return object.criteria1;
case 2:
return object.criteria2;
case 3:
return object.criteria3;
default:
return object.criteria1;
}
}
and getDataByCriteria add an arg to indicate which filed to use.
Or you can just use macro to implement getDataByCriteria.
You tagged it C++11, so use variadic templates.
class VariadicTest
{
public:
VariadicTest()
{
std::map<std::string, int> test1 = getDataByCriteria(testValues, criteria1);
std::map<std::string, int> test2 = getDataByCriteria(testValues, criteria2);
std::map<std::string, int> test3 = getDataByCriteria(testValues, criteria1, criteria2);
std::map<std::string, int> test4 = getDataByCriteria(testValues, criteria1, criteria3);
}
private:
std::string criteria1 = { "Hello" };
std::string criteria2 = { "world" };
std::string criteria3 = { "." };
std::vector<CustomClass> testValues = { {"Hello",1}, {"world",2},{ "!",3 } };
template<typename T> std::map<std::string, int> getDataByCriteria(std::vector<CustomClass> values, T criteria)
{
std::map<std::string, int> result;
//do whatever is needed here to filter values
for (auto v : values)
{
if (v.identifier == criteria)
{
result[values[0].identifier] = values[0].value;
}
}
return result;
}
template<typename T, typename... Args> std::map<std::string, int> getDataByCriteria(std::vector<CustomClass> values, T firstCriteria, Args... args)
{
std::map<std::string, int> result = getDataByCriteria(values, firstCriteria);
std::map<std::string, int> trailer = getDataByCriteria(values, args...);
result.insert(trailer.begin(), trailer.end());
return result;
}
};
You do not specify the actual operations to be done under the various conditions of the criteria being met so it is hard to say how much they actually can be combined.
Here is a possible solution using the std::accumulate() of the STL along with some additional functionality. This example was compiled with Visual Studio 2015.
This approach would make sense if most of the functionality can be combined into a reasonably small accumulation function because most of the criteria are handled in the same way. Or you could have the accumulate_op() function call other functions for specific cases while handling the general case itself.
You might take this as a beginning and make the appropriate modifications.
One such modification may be to get rid of the use of std::map to maintain state. Since using this approach you would iterate through the std::vector doing the accumulation based on the criteria, I am not sure you would even need to use std::map to remember anything if you are accumulating as you go.
// map_fold.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <numeric>
// this is the class with data and criteria
class CustomClass
{
public:
CustomClass() : dataToBeCombined(0) {}
std::string criteria1;
std::string criteria2;
std::string criteria3;
//... and others criteria
int dataToBeCombined;
// other code
};
// This is the class that will contain the results as we accumulate across the
// vector of CustomClass items.
class Criteria_Result {
public:
Criteria_Result() : dataToBeCombined(0) {}
CustomClass myCriteria;
std::map<std::string, int> result1;
std::map<std::string, int> result2;
std::map<std::string, int> result3;
int dataToBeCombined;
};
// This is the accumulation function we provide to std::accumulate().
// This function will build our results.
class accumulate_op {
public:
Criteria_Result * operator ()(Criteria_Result * x, CustomClass &item);
};
Criteria_Result * accumulate_op::operator ()(Criteria_Result *result, CustomClass &item)
{
if (!result->myCriteria.criteria1.empty() && !item.criteria1.empty()) {
std::map<std::string, int>::iterator it1 = result->result1.find(item.criteria1);
if (it1 == result->result1.end()) // if such of key doesn't exists
{
result->result1.insert(std::make_pair(item.criteria1, item.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
it1->second += item.dataToBeCombined;
}
result->dataToBeCombined += item.dataToBeCombined;
}
if (!result->myCriteria.criteria2.empty() && !item.criteria2.empty()) {
std::map<std::string, int>::iterator it2 = result->result2.find(item.criteria2);
if (it2 == result->result2.end()) // if such of key doesn't exists
{
result->result2.insert(std::make_pair(item.criteria2, item.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
it2->second += item.dataToBeCombined;
}
result->dataToBeCombined += item.dataToBeCombined;
}
if (!result->myCriteria.criteria3.empty() && !item.criteria3.empty()) {
std::map<std::string, int>::iterator it3 = result->result3.find(item.criteria3);
if (it3 == result->result3.end()) // if such of key doesn't exists
{
result->result3.insert(std::make_pair(item.criteria3, item.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
it3->second += item.dataToBeCombined;
}
result->dataToBeCombined += item.dataToBeCombined;
}
return result;
}
int main()
{
Criteria_Result result;
std::vector<CustomClass> aVector;
// set up the criteria for the search
result.myCriteria.criteria1 = "string1";
result.myCriteria.criteria2 = "string2";
for (int i = 0; i < 10; i++) {
CustomClass xx;
xx.dataToBeCombined = i;
if (i % 2) {
xx.criteria1 = "string";
}
else {
xx.criteria1 = "string1";
}
if (i % 3) {
xx.criteria2 = "string";
}
else {
xx.criteria2 = "string2";
}
aVector.push_back (xx);
}
// fold the vector into our results.
std::accumulate (aVector.begin(), aVector.end(), &result, accumulate_op());
std::cout << "Total Data to be combined " << result.dataToBeCombined << std::endl;
std::cout << " result1 list " << std::endl;
for (auto jj : result.result1) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result2 list " << std::endl;
for (auto jj : result.result2) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result3 list " << std::endl;
for (auto jj : result.result3) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " Trial two \n\n" << std::endl;
result.myCriteria.criteria2 = "";
result.result1.clear();
result.result2.clear();
result.result3.clear();
result.dataToBeCombined = 0;
// fold the vector into our results.
std::accumulate(aVector.begin(), aVector.end(), &result, accumulate_op());
std::cout << "Total Data to be combined " << result.dataToBeCombined << std::endl;
std::cout << " result1 list " << std::endl;
for (auto jj : result.result1) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result2 list " << std::endl;
for (auto jj : result.result2) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result3 list " << std::endl;
for (auto jj : result.result3) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
return 0;
}
This produces the output as follows:
Total Data to be combined 90
result1 list
string 25
string1 20
result2 list
string 27
string2 18
result3 list
Trial two
Total Data to be combined 45
result1 list
string 25
string1 20
result2 list
result3 list