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

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

Related

Using two different copies of an identical std::view

I'm trying to use views in a commercial application, and noticed an inconsistency between gcc and Visual Studio.
In the code below, calling transformed() twice returns two different, apparently incompatible views. In gcc 11 (on godbolt), the code executes without issue, even with extra debugging, but in Visual Studio 16.11 with -std:c++latest, it asserts:
cannot compare incompatible transform_view iterators
I would like my function to be callable just as if it were returning a const std::vector<std::pair<int, int>> & so the caller doesn't have to worry about temporaries. It seems that I could make my transformed view a member of my class, initialize it in the constructor, and return that, but I don't even know how to declare it.
I'm assuming that Visual Studio is correct and my code is illegal, but even if my code should be legal, it still has to work. We have a 10,000,000-line code base and a lot of non-expert C++ programmers, and I need the core to be robust and not have hidden gotchas like this.
#include <iostream>
#include <ranges>
#include <vector>
struct X
{
std::vector<int> m_values{ 1,2,3 };
auto transformed() const
{
return std::ranges::views::transform(m_values, [](int i) {
return std::pair{ i, i + i };
});
}
};
int main()
{
X x;
for (auto [a, b] : x.transformed())
std::cout << a << " " << b << std::endl;
if (x.transformed().begin() != x.transformed().end()) // asserts in visual studio.
std::cout << "not empty";
return 0;
}
https://godbolt.org/z/hPWYGn9dY
It seems that I could make my transformed view a member of my class,
initialize it in the constructor, and return that, but I don't even
know how to declare it.
You can turn X into a template class, and construct the member transform_view through the passed lambda, something like this:
#include <iostream>
#include <ranges>
#include <vector>
template<class F>
struct X {
std::vector<int> m_values{1,2,3};
decltype(m_values | std::views::transform(std::declval<F>())) m_transformed;
X(F fun) : m_transformed(m_values | std::views::transform(std::move(fun))) { }
const auto& transformed() const { return m_transformed; }
};
int main() {
X x([](int i) { return std::pair{i, i + i}; });
for (auto [a, b] : x.transformed())
std::cout << a << " " << b << std::endl;
if (x.transformed().begin() != x.transformed().end())
std::cout << "not empty";
}
Demo.
Another way is to use std::function, which makes your X need not be a template class:
struct X {
using Fun = std::function<std::pair<int, int>(int)>;
std::vector<int> m_values{1,2,3};
decltype(m_values | std::views::transform(Fun{})) m_transformed;
X(Fun fun = [](int i) { return std::pair{i, i + i}; })
: m_transformed(m_values | std::views::transform(std::move(fun))) { }
const auto& transformed() const { return m_transformed; }
};
Demo.

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.

Sort according to first vector of pair in C++

I have a data structure of the following form:
vector<pair<vector<unsigned>,vector<unsigned> > a;
Now I want to sort the vector "a" according to first vector of pair. E.g. in the example given below, I want to rank according to the following:
(((1,2,4),(89,29)) , ((1,3),(67,90))). As size of a3 is greater than size of a1, therefore ranking is done first according to a3 and then a1.
E.g. If
vector<unsigned> a1,a3;
a1.push_back(1); a1.push_back(3);
vector<unsigned> a2,a4;
a2.push_back(67); a2.push_back(90);
a.push_back(make_pair(a1,a2));
a3.push_back(1); a3.push_back(2); a3.push_back(4);
a4.push_back(89); a4.push_back(29);
a.push_back(make_pair(a3,a4));
I know I can do the sorting using bubble sort. But is there some other way in C++ like STL function to do the same.
Just use std::sort with a function object (a lambda here) that compares the first element of each pair (and which uses the operator< from std::vector)
#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>
using namespace std;
int main()
{
vector<pair<vector<unsigned>,vector<unsigned> >> a = {{{1,3},{67, 90}}, {{1,2,4},{89,29}}};
sort(begin(a), end(a), [](auto const& L, auto const& R) {
return L.first < R.first;
});
for (auto const& elem : a) {
std::cout << "{{";
for (auto const& u : elem.first)
std::cout << u << ", ";
std::cout << "},";
std::cout << "{";
for (auto const& u : elem.second)
std::cout << u << ", ";
std::cout << "}},";
}
}
Live Example using C++14.
You can adapt it to C++98 by writing the range-based for loop using iterators / indices, and by writing a separate function object that you pass to std::sort. Oh, and you would also have to use the awkward push_back initialization instead of the more convenient initalizer-lists from C++11 and beyond.

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 .

Why can't for_each modify its functor argument?

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.