Why can't for_each modify its functor argument? - c++

http://www.cplusplus.com/reference/algorithm/for_each/
Unary function taking an element in
the range as argument. This can either
be a pointer to a function or an
object whose class overloads
operator(). Its return value, if any,
is ignored.
According to this article, I expected that for_each actually modifies the object given as its third argument, but it seems like for_each operates on a temporary object, and doesn't even modify the object given to it.
So, why is it implemented in that way? It seems much less useful. Or did I misunderstand something and my code below contains errors?
#include <iostream>
#include <vector>
#include <algorithm>
template <class T> struct Multiplicator{
T mresult;
public:
const T& result() const{return mresult;}
Multiplicator(T init_result = 1){
mresult = init_result;
}
void operator()(T element){
mresult *= element;
std::cout << element << " "; // debug print
}
};
int main()
{
std::vector<double> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
Multiplicator<double> multiply;
std::for_each(vec.begin(),vec.end(),multiply);
std::cout << "\nResult: " << multiply.result() << std::endl;
return 0;
}
Expected output:
1 2 3 Result: 6
But got following output:
1 2 3 Result: 1

The function object is taken by value. for_each returns the function object, so if you change it to:
multiply = std::for_each(vec.begin(),vec.end(),multiply);
you get the expected output.

While James is correct, using std::accumulate with std::multiplies would be more correct, probably:
#include <iostream>
#include <functional>
#include <numeric>
#include <vector>
int main(void)
{
std::vector<double> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
double result = std::accumulate(vec.begin(), vec.end(),
1.0, std::multiplies<double>());
std::cout << "\nResult: " << result << std::endl;
}
With your for_each version, you don't really need to copy the functor again, rather:
double result = std::for_each(vec.begin(), vec.end(), multiply).result();
Or C++0x, for fun:
double result = 1;
std::for_each(vec.begin(), vec.end(), [&](double pX){ result *= pX; });

The semantics of For_each dont fit into what you are trying to do. accumulate does exactly what you are trying, use that instead.

Related

Passing std::ranges::views as parameters in C++20

I have a method that prints a list of integers (my actual method is a bit more complicated but it is also read-only):
void printElements(const std::vector<int> &integersList)
{
std::for_each(integersList.begin(), integersList.end(), [](const auto& e){
std::cout << e << "\n";
});
}
Now suppose I have the following vector:
std::vector<int> vec{1,2,3,4,5,6,7,8,9,10};
Then I want to print even numbers only. To do this, I thought of employing the new std::ranges feature in C++20. I know that you can do this as follows:
auto evenList = vec | std::views::filter([](auto i){ return i % 2 == 0; });
Now I would like to call printElements(evenList), however this obviously won't compile. What would be the solution to this? Also can I write a single function that will both print a std::vector and an object of the same type as my evenList? Or do I need to write two separate functions?
You can make printElements take any object by making it a function template. This will instantiate it for views as well as vectors.
#include <algorithm>
#include <iostream>
#include <ranges>
void printElements(std::ranges::input_range auto&& range) {
std::ranges::for_each(range, [](const auto& e) { std::cout << e << '\n'; });
}
Demo
or:
// ...
#include <iterator>
void printElements(std::ranges::input_range auto&& range) {
using T = std::ranges::range_value_t<decltype(range)>;
std::ranges::copy(range, std::ostream_iterator<T>(std::cout, "\n"));
}
Demo

Why does 'for_each' not reading function object

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template <class T>
class Sum {
public:
Sum(T i = 0) : res(i) {}
void operator()(T x) { res =res + x; }
T result() const { return res; }
private:
T res;
};
int main() {
Sum<int> s;
vector<int> vec;
vec.insert(vec.begin(), 10);
vec.insert(vec.begin()+1, 10);
vec.insert(vec.begin()+2, 10);
vector<int>::iterator itr = vec.begin();
cout << *itr << endl;
for_each(vec.begin(), vec.end(), s);
cout << "sum is" << s.result() << endl;
return 0;
}
This is my code. I want to add vec values in class Sum res. for_each should be calling s's operator(), so the result should be 30, but it shows 0.
I think adding value in vector has no problem. Why is the s.operator() is not working?
for_each takes its third argument by value which means every invocation of operator() affects a completely separate copy of s. There's an algorithm for exactly what you're doing called std::accumulate, but if you want this to work with for_each you need to pass s "by reference" by using std::ref from <functional>.
for_each(vec.begin(), vec.end(), ref(s));
for_each returns a copy of the passed-in functor that provides the "result" of the iteration (whatever the result is). Change your call to:
auto s = for_each(vec.begin(), vec.end(), Sum<int>());

Preserving insertion order on a std::set or std::unordered_set

Before marking this as duplicate, I have been here, here, and here, a duplicate of the first.
I'm aware of boost::multi_index, and use an environment where I lack it, and that a std::unordered_set is not bound to store elements in a deterministic insertion order.
I find the concept of using two containers, say an additional std::vector as uncouth.
What I would love is a solution involving a comparator that I can use in a std::set's template parameters (clarification, this could be a trivial functor struct, containing a bool operator()() overload, a regular function, or a lambda). Is it possible?
Addenda
Initialization must occur through a std:: container's begin iterator/end iterator constructor, such as in this snippet.
std::string str; cin >> str;
std::set<char>(str.begin(), str.end());
Also, another interesting use-case would be to create a dumb hash wrapping functor that allows insertion order to be pushed in to a std::unordered_set's template parameter.
You cannot directly have a lambda expression as the set's template parameter, because a lambda expression is a value, and the set's template parameter is a type. The obvious correction of the question, whether a construction using a lambda and decltype can work, leads to the interesting problem that a lambda expression denotes a unique type (a "closure type"), so you can never make two separate lambda expressions of the same closure type.*
However, in a more abstract sense what you want can be achieved in a local context using template argument deduction, for example:
template <typename F>
int f(int* first, int* last, F comp)
{
std::set<int, F> s(comp);
while (first != last) s.insert(*first++);
...
}
Now you can call f with a lambda expression as the argument, thus effectively "using a lambda as the set's comparator". Or, for a simpler example, you could just have a named variable for the lambda (putting all the template deduction into a single auto:
auto comp = [](...) { ... };
std::set<int, decltype(comp)> s(comp);
*) There is a proposal to allow lambdas in unevaluated contexts to address this point, but its outlook is uncertain. It has interesting side effects like making closure types affect name mangling.
An adt that preserves the order of insertion is an std::vector.
You can just as easily wrap it like this to get an std::set-like behavior:
#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
using namespace std;
template < typename T >
class VectorSet : public vector<T> {
public:
using iterator = typename vector<T>::iterator;
using value_type = typename vector<T>::value_type;
pair<iterator, bool> insert (const value_type& val) {
auto it = ::find(this->begin(), this->end(), val);
if (it == this->end())
it = ::vector<T>::insert(this->end(), val);
return pair<iterator, bool>(it, true);
}
};
int main()
{
VectorSet<int> my;
my.insert(1);
my.insert(4);
my.insert(3);
my.insert(4);
for (auto & v : my) {
cout << v << endl;
}
return 0;
}
You cannot, unless you use additional indexes. Two approaches:
1. using an explicit index
Live On Coliru
#include <set>
#include <vector>
#include <functional>
#include <algorithm>
using namespace std;
#include <iostream>
string read_word() {
string str;
cin >> str;
return str;
}
int main() {
using Ref = std::reference_wrapper<char const>;
auto const str = read_word();
std::cout << "Word: " << str << "\n";
auto v = [&]() -> vector<Ref> {
set<Ref> u(str.begin(), str.end());
return {u.begin(), u.end()};
}();
std::cout << "Unique: " << string(v.begin(), v.end()) << "\n";
auto pos = [str](char ch) { return str.find(ch); };
std::sort(v.begin(), v.end(), [pos](auto& a, auto& b) { return pos(a) < pos(b); });
std::cout << "Insertion: " << string(v.begin(), v.end()) << "\n";
}
Prints e.g.
Word: pineapple
Unique: aeilnp
Insertion: pineal
2. using Boost Multi-Index
Same deal
Live On Coliru
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
namespace bmi = boost::multi_index;
using Index = bmi::multi_index_container<char,
bmi::indexed_by<
bmi::sequenced<>,
bmi::ordered_unique<bmi::tag<struct unique>, bmi::identity<char> >
> > ;
#include <iostream>
std::string read_word() {
std::string str;
std::cin >> str;
return str;
}
int main() {
auto const str = read_word();
std::cout << "Word: " << str << "\n";
Index idx(str.begin(), str.end());
std::cout << "Insertion: " << std::string(idx.begin(), idx.end()) << "\n";
auto& u = idx.get<unique>();
std::cout << "Unique: " << std::string(u.begin(), u.end()) << "\n";
}
Prints
Word: pineapple
Insertion: pineal
Unique: aeilnp
I thought a weird solution (though not one involving any sets) could be to use a std::map of the element type and std::time_point as the key type. That will ensure insertion order if not anything at all.

std::transform() passing reference in the function parameter

My question/concern is about the function parameter to be used in std::transform().
In the following codes, if I used a "pass-by-reference" in the integer parameter i of the squared function (i.e. int squared(int i)), it does not compile.
I have to change it to pass by value so that it compiles. Can anyone please tell me why and if this is a cosntraint for using std::transform()?
The std::for_each() is fine with using both "pass-by-value" and "pass-by-reference" approaches (as shown in print()).
Thanks in advance.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int squared(int i);
void print(int &i);
int main()
{
std::vector<int> intVec;
std::vector<int> newIntVec;
for (int i = 0; i < 10; ++i)
intVec.push_back(i);
std::for_each(intVec.begin(), intVec.end(), print);
std::cout << std::endl;
std::transform(intVec.begin(), intVec.end(), std::back_inserter(newIntVec), squared);
std::for_each(newIntVec.begin(), newIntVec.end(), print);
std::cout << std::endl;
return 0;
}
int squared(int i)
{
return i*i;
}
void print(int &i)
{
std::cout << i << " ";
}
For a std::transform, the operator should have no side-effects (it should take the input and provide an output). So you should try making the reference const:
int squared(const int &i)
{
return i*i;
}
To quote from CPP Reference, "[the function] must not have side effects" (C++), and also "[the function] must not invalidate any iterators, including the end iterators, or modify any elements of the ranges involved." (C++11)
This basically means that what gets passed to your function should be considered immutable... hence if you pass by reference, it should be a const reference.
Conversely, std::for_each operates on the series of data you pass it, meaning that the values can be modified.

Initialise an Stl Vector of type T* from an array of type T

if I have an array such as:
struct S {... };
S m_aArr[256];
and I want to use this to construct a vector such as:
std::vector<S*> m_vecS;
Is there anyway to do this rather than looping through and pushing back &m_aArr[i] ?
I understand that I cannot use the conventional method of using std::begin and std::end on the array since the vector is one of pointers and the original array is one of objects, and so we cannot just pass in a block of memory.
You could use the standard library to do the iteration and pushing back for you:
std::transform(std::begin(m_aArr), std::end(m_aArr),
std::back_inserter(m_vecS), std::addressof<S>);
This will transform each of the elements in m_aArr by applying the std::addressof<S> function to them. Each of the transformed elements is then push_backed into m_vecS by the std::back_inserter iterator.
To do this prior to C++11, you won't have access to std::begin, std::end, or std::addressof, so it'll look more like this:
std::transform(m_aArr, m_aArr + 256, std::back_inserter(m_vecS), boost::addressof<S>);
This uses boost::addressof.
You could let std::transform perform the loop:
transform(std::begin(a), std::end(a), std::back_inserter(v),
[] (S& s) { return &s; });
Notice, that you do not need to fully qualify the name std::transform, because the function name will by found by ADL.
This is a complete program to test its behavior:
#include <iostream>
#include <vector>
#include <algorithm> // <== Required for std::transform
#include <iterator> // <== Required for std::back_inserter, std::begin, std::end
struct S
{
S() : i(0) { }
S(int i_) : i(i_) { }
int i;
};
int main()
{
S a[256] = { 42 }; // Copy-initializes first element from 42,
// default-constructs all other elements
std::vector<S*> v;
transform(std::begin(a), std::end(a), std::back_inserter(v),
[] (S& s) { return &s; });
std::cout << v.size() << std::endl; // Prints 256
std::cout << v[0]->i << std::endl; // Prints 42
std::cout << v[1]->i << std::endl; // Prints 0
}
And here is a live example.
A solution using std::generate_n() that performs the single allocation of the std::vector instead of potentially multiple via std::vector::push_back():
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
struct S {};
S a[128];
S* ap = a;
std::vector<S*> v(sizeof(a)/sizeof(a[0]));
std::generate_n(std::begin(v), v.size(), [&]() { return ap++; });
for (size_t i = 0; i < v.size(); i++)
{
if (&a[i] != v[i]) // Ensure same address at each element.
{
std::cerr << "Error\n";
break;
}
}
return 0;
}
See online at http://ideone.com/73nKST .