Using Lambda Function with find_if C++20 - c++

I am getting a syntax error on the line
if (auto result = ranges::find_if(height.begi with a red squiggly line under find_if:
no instance of overloaded function matches the argument list
auto is_gt_or_eq = [height, i](int x) { height[x] >= height[i]; };
if (auto result = ranges::find_if(height.begin(), height.end(), is_gt_or_eq); result != height.end()) {
std::cout << "First larger or equal to element in height: " << *result << '\n';
}
else {
std::cout << "No larger or equal to element in height\n";
}
I found this similar code on cppreference, and it does run:
auto is_even = [](int x) { return x % 2 == 0; };
if (auto result = ranges::find_if(height.begin(), height.end(), is_even); result != height.end()) {
std::cout << "First even element in height: " << *result << '\n';
}
else {
std::cout << "No even elements in height\n";
}
I believe the error is in this line of code:
auto is_gt_or_eq = [height, i](int x) { height[x] >= height[i]; };

To start with, you forgot the return keyword for the return statement. So your lambda is returning void by default, thus not a valid predicate. The library won't allow you to call its function due to this mismatch.
Beyond that, x is (a copy of) the element of height, not an index of the container. There is no need to access the container again for the element in the lambda. So, the simplest fix is
auto is_gt_or_eq = [height, i](int x) { return x >= height[i]; };
There's also no need to constantly re-access height[i] in the lambda. It's not a bad idea to just capture that value instead.
auto is_gt_or_eq = [hi = height[i]](int x) { return x >= hi; };
Your lambda is now smaller, more inline-able and (to me at least) more readable.

Related

Why iota_view allows different integer template arguments?

I have the following broken code, from what I can tell problem here is that iota(0, n)
returns me a iota_view<int,int64> and then obviously int can never be int64 that is greater than INT_MAX.
Easy fix is to just use iota(0LL, n), but that seems error prone.
int main() {
const int64_t n = 16LL*1024*1024*1024;
auto ints = std::ranges::views::iota(0, n) |
std::views::transform([](int64_t x) { return x * 10; });
for (int64_t lookup : {49LL, 50LL, 51LL}) {
const auto it = std::ranges::lower_bound(ints, lookup);
if (it != ints.end()) {
std::cout << *it << std::endl;
std::cout << *it.base() << std::endl;
}
}
}
My best guess is that iota_view wants to work with "weird" second type, like some kind of +INF type, so that is why it needs 2 types, and nobody thought of forcing the first argument to match the second one if they are both ints.

Can I use std::find() with lambda function in vc++ [VS 2019]?

OS: Windows 10 Pro 64-bit(10.0, build 18362)
IDE: Visual Studio 2019(Version 16.4.3) [Before this I was using VS 2010]
Language: c++ and VC++
Here's my simple code in which i just want to find element 3 and print it if found
std::vector<int> intVect;
for (int counter = 0; counter < 5; counter++)
{
intVect.push_back(counter);
}
std::find(intVect.begin(), intVect.end(), [](int a)
{
if (a == 3)
{
std::cout << "Item Found.." << std::endl;
}
});
The probelm is when i compile this code it's giving me error like below:
Error C2678 binary '==' : no operator found which takes a left-hand
operand of type 'int' (or there is no acceptable conversion)
c:\Program Files (x86)\Microsoft Visual Studio
10.0\VC\include\algorithm 41
You can do this easily without a lambda:
auto val = std::find(intVect.begin(), intVect.end(), 3);
if (val != intVect.end()) {
std::cout << "Value found\n";
}
However, it's also possible to use a lambda, but you need to use std::find_if (available in the same header as std::find: <algorithm>):
auto val = std::find_if(intVect.begin(), intVect.end(), [](int i) { return i == 3; });
if (val != intVect.end()) {
std::cout << "Value found\n";
}
But there's really no point. You'd only use find_if in this case if you had something more complex, like a struct and you were searching for a particular member:
struct complex {
int id;
...
};
std::vector<complex> things = ...;
auto val = std::find_if(things.begin(), things.end(), [](const complex& c) { return c.id == 3; });
if (val != things.end()) {
std::cout << "Value found\n";
}
std::find expects a value to be compared to elements, so it should be
if (std::find(intVect.begin(), intVect.end(), 3) != intVect.end())
{
std::cout << "Item Found.." << std::endl;
}
If you want to use a lambda as predicate, you should use std::find_if; note that the lambda should return bool.
if (std::find_if(intVect.begin(), intVect.end(), [](int a) { return (a == 3); }) != intVect.end())
{
std::cout << "Item Found.." << std::endl;
}

Returning unique_ptr from std::for_each + lambda functions

I am facing a problem with returning std::moved unique_pointers to a lambda. Once I have moved the pointer to the lambda function how do I take the ownership back from the lambda?
In the following code I am demonstrating my problem. I have taken a snipped out from code-base and moved everything to main to explain the problem better. First question is marked as "QUESTION 1" - where I want to understand if I am correct in using (*v) for accessing my vector.
The code creates a vector of few numbers, and then iterates over the vector to mark the bits in the bitmap. I think the bits are marked correctly as I am able to print them in the lambda itself. After marking the bits, I want the ownership back. How do I do it? I need to return the bitmap pointer to the caller function.
How to take the ownership back in a STANDARD way from a lambda rather than hacking around the passed unique_ptr or avoiding moving the pointer to lambda. Does cpp17 support this?
Compile the code with g++ -std=c++17
#include <memory>
#include <algorithm>
#include <iostream>
#include <vector>
int main () {
int increments = 10;
int numberOfElements = 10;
/* Create the vector */
auto v = std::make_unique<std::vector<int>>(std::vector<int> (numberOfElements));
/* QUESTION 1 - is (*v) the right way to access it or there is a better way. */
std::generate((*v).begin(), (*v).end(), [n=0, increments]() mutable { n = n + increments; return n;});
/* Print the generated elements */
std::cout << "\nPrinting the generated elements ";
std::for_each((*v).begin(), (*v).end(), [](int n) { std::cout<<" " << n;});
/* Int find the maximum element */
int maxElement = *(std::max_element((*v).begin(), (*v).end()));
/* Making a bitmap of the elements */
std::cout << "\nPrinting the maxElement " << maxElement;
auto bitmap = std::make_unique<std::vector <bool>> (std::vector<bool>(maxElement + 1));
/* Now setting all the elements in the vector to true in the bitmap. */
for_each((*v).begin(), (*v).end(), [bmap = std::move(bitmap)](int n) {
(*bmap).at(n) = true;
if ((*bmap).at(n) == true) {
std::cout << "\nBit "<< n <<" marked";
}
});
/*******************************************************
* Question 2 : I now need the ownership of bitmap back.
* How to do it ?. Bitmap is now null after moving it to the lambda.
*/
if (bitmap) {
std::cout << "\nafter reset, ptr is not NULL ";
} else if (bitmap == nullptr) {
std::cout << "\nbitmap is null";
}
}
QUESTION 1 - is (*v) the right way to access it or there is a better way.
Alternative is using ->, so v->begin() instead of (*v).begin().
but it is strange to use unique_ptr for vector.
You might simply do:
std::vector<int> v(numberOfElements);
std::generate(v.begin(), v.end(),
[n=0, increments]() mutable { n = n + increments; return n;});
std::cout << "\nPrinting the generated elements ";
for (int e : v) { std::cout << " " << e; };
int maxElement = *std::max_element(v.begin(), v.end());
// ...
Question 2 : I now need the ownership of bitmap back. How to do it ?
You cannot do it with lambda, in your case capturing by reference do the job (even if the lambda would own the vector, the for_each "block" doesn't):
std::vector<bool> bitmap(maxElement + 1);
for_each(v.begin(), v.end(), [&bmap](int n) {
bmap.at(n) = true;
std::cout << "\nBit "<< n <<" marked";
});
assert(!bitmap.empty());
if (bitmap.empty()) {
std::cout << "\nbitmap is empty";
} else if (bitmap == nullptr) {
std::cout << "\nbitmap is NOT empty";
}
If you replace your lambda with your own functor, you might do something like
class MyMarker
{
public:
MyMarker(std::vector<bool>&& bitmap) : bitmap(std::move(bitmap)) {}
MyMarker(const MyMarker&) = delete;
MyMarker& operator =(const MyMarker&) = delete;
void operator() (int n) const
{
bitmap.at(n) = true;
std::cout << "\nBit "<< n <<" marked";
}
std::vector<bool> TakeBack() { return std::move(bitmap); }
private:
std::vector<bool> bitmap;
};
and then:
std::vector<bool> bitmap(maxElement + 1);
MyMarker myMarker(std::move(bitmap));
assert(bitmap.empty());
std::for_each(v.begin(), v.end(), myMarker);
bitmap = myMarker.TakeBack();
assert(!bitmap.empty());

How to create a map with custom class/comparator as key

I have a class named ItemType. It has two members - both double, named m_t and m_f. Two items of type ItemType are considered to be equal if these two members differ from each other within respective tolerance levels. With this logic, the comparator function is so defined as well. However, when I insert objects of this type as key into a map, only one key is produced in the map, even though at least three such keys should be present:
#include <iostream>
#include <string>
#include <map>
#include <cmath>
#include <vector>
using namespace std;
class ItemKey
{
public:
ItemKey(double t, double f)
{
m_t = t;
m_f = f;
}
double m_t;
double m_f;
double m_tEpsilon = 3;
double m_fEpsilon = 0.1;
bool operator<(const ItemKey& itemKey) const
{
int s_cmp = (abs(itemKey.m_f - m_f) > m_fEpsilon);
if (s_cmp == 0)
{
return (abs(itemKey.m_t - m_t) > m_tEpsilon);
}
return s_cmp < 0;
}
};
int main()
{
// The pairs are the respective values of m_t and m_f.
vector<pair<double, double>> pairs;
// These two should belong in one bucket -> (109.9, 9.0), because m_f differs by 0.09 and m_t differs by just 1
pairs.emplace_back(109.9, 9.0);
pairs.emplace_back(110.9, 9.09);
// This one is separate from above two beause even though m_t is in range, m_f is beyong tolerance level
pairs.emplace_back(109.5, 10.0);
// Same for this as well, here both m_t and m_f are beyong tolerance of any of the two categories found above
pairs.emplace_back(119.9, 19.0);
// This one matches the second bucket - (109.5, 10.0)
pairs.emplace_back(109.9, 10.05);
// And this one too.
pairs.emplace_back(111.9, 9.87);
map<ItemKey, size_t> itemMap;
for (const auto& item: pairs)
{
ItemKey key(item.first, item.second);
auto iter = itemMap.find(key);
if (iter == itemMap.end())
{
itemMap[key] = 1;
}
else
{
itemMap[iter->first] = itemMap[iter->first] + 1;
}
}
// The map should have three keys - (109.9, 9.0) -> count 2, (109.5, 10.0) -> count 3 and (119.9, 19.0) -> count 1
cout << itemMap.size();
}
However, the map seems to have only 1 key. How do I make it work as expected?
Why isn't your version working?
You did well to create your own comparison function. To answer your question, you have an error in your operator<() function such that only returns true if m_f is outside of tolerance and m_t is within tolerance, which I'm guessing is not what you desired. Let's take a look.
int s_cmp = (abs(itemKey.m_f - m_f) > m_fEpsilon);
The above line basically is checking whether this->m_f and itemKey.m_f are within tolerance of eachother (meaning equal to each other). That is probably what was intended. Then you say
if (s_cmp == 0)
{
return (abs(itemKey.m_t - m_t) > m_tEpsilon);
}
If s_cmp is true, then it will have the value of 1, and it will have a value of 0 for false (meaning that they are not within tolerance of each other). Then you return true if the m_t value is within tolerance. Up to this point, you return true if m_f is not equal (according to tolerance) and if m_t is equal (according to tolerance). Then your last line of code
return s_cmp < 0;
will return true always since a boolean converted to an integer cannot ever be negative.
How to get it working?
#include <iostream>
#include <string>
#include <map>
#include <cmath>
#include <vector>
struct ItemKey
{
double m_t;
double m_f;
static constexpr double t_eps = 3;
static constexpr double f_eps = 0.1;
ItemKey(double t, double f) : m_t(t), m_f(f) {}
bool operator<(const ItemKey& other) const
{
// Here it is assumed that f_eps and t_eps are positive
// We also ignore overflow, underflow, and NaN
// This is written for readability, and assumed the compiler will be
// able to optimize it.
auto fuzzy_less_than = [] (double a, double b, double eps) {
return a < b - eps;
};
bool f_is_less_than = fuzzy_less_than(this->m_f, other.m_f, f_eps);
bool f_is_greater_than = fuzzy_less_than(other.m_f, this->m_f, f_eps);
bool f_is_equal = !f_is_less_than && !f_is_greater_than;
bool t_is_less_than = fuzzy_less_than(this->m_t, other.m_t, t_eps);
return f_is_less_than || (f_is_equal && t_is_less_than);
}
};
int main()
{
using namespace std;
// The pairs are the respective values of m_t and m_f.
vector<pair<double, double>> pairs;
// These two should belong in one bucket
// -> (109.9, 9.0), because m_f differs by 0.09 and m_t differs by just 1
pairs.emplace_back(109.9, 9.0);
pairs.emplace_back(110.9, 9.09);
// This one is separate from above two beause even though m_t is in range,
// m_f is beyong tolerance level
pairs.emplace_back(109.5, 10.0);
// Same for this as well, here both m_t and m_f are beyong tolerance of any
// of the two categories found above
pairs.emplace_back(119.9, 19.0);
// This one matches the second bucket - (109.5, 10.0)
pairs.emplace_back(109.9, 10.05);
// And this one too.
pairs.emplace_back(111.9, 9.87);
map<ItemKey, size_t> itemMap;
for (const auto& item: pairs)
{
ItemKey key(item.first, item.second);
auto iter = itemMap.find(key);
if (iter == itemMap.end())
{
itemMap[key] = 1;
}
else
{
itemMap[iter->first] = itemMap[iter->first] + 1;
}
}
// The map should have three keys
// - (109.9, 9.0) -> count 2
// - (109.5, 10.0) -> count 3
// - (119.9, 19.0) -> count 1
cout << itemMap.size();
cout << "itemMap contents:" << endl;
for (auto& item : itemMap) {
cout << " (" << item.first << ", " << ")" << endl;
}
return 0;
}
There are a few things I changed above. I have a few suggestions also unrelated to the programming mistake:
Do not store boolean values into integer variables.
There's a reason that C++ introduced the bool type.
Write your code to be readable and in a way that the compiler
can easily optimize. You may notice I used a lambda expression
and multiple booleans. Smart compilers will inline the calls to
that lambda expression since it is only used within the local scope.
Also smart compilers can simplify boolean logic and make it
performant for me.
The m_tEpsilon and m_fEpsilon are probably not good to be
changable variables of the class. In fact, it may be bad if one
object has a different epsilon than another one. If that were the
case, which do you use when you do the < operator? For this
reason, I set them as static const variables in the class.
For constructors, it is better to initialize your variables in the
initializer list rather than in the body of the constructor. That
is unless you are doing dynamic resource allocation, then you would
want to do it in the constructor and make sure to clean it up if
you end up throwing an exception (preferrably using the RAII
pattern). I'm starting to get too far off topic :)
Even though class and struct are basically identical except for
the default protection level (class is private by default and
struct is public by default). It is convention to have it as a
struct if you want direct access to the member variables. Although,
in this case, I would probably set your class as immutable. To do
that, set the m_t and m_f as private variables and have a getter
m() and f(). It might be a bad idea to modify an ItemKey
instance in a map after it has been inserted.
Potential problems with this approach
One of the problems you have with your approach here is that it will be dependent on the order in which you add elements. Consider the following pairs to be added: (3.0, 10.0) (5.0, 10.0) (7.0, 10.0). If we add them in that order, we will get (3.0, 10.0) (7.0, 10.0), since (5.0, 10.0) was deemed to be equal to (3.0, 10.0). But what if we were to have inserted (5.0, 10.0) first, then the other two? Well then the list would only have one element, (5.0, 10.0), since bother of the others would be considered equal to this one.
Instead, I would like to suggest that you use std::multiset instead, of course this will depend on your application. Consider these tests:
void simple_test_map() {
std::map<ItemKey, size_t> counter1;
counter1[{3.0, 10.0}] += 1;
counter1[{5.0, 10.0}] += 1;
counter1[{7.0, 10.0}] += 1;
for (auto &itempair : counter1) {
std::cout << "simple_test_map()::counter1: ("
<< itempair.first.m_t << ", "
<< itempair.first.m_f << ") - "
<< itempair.second << "\n";
}
std::cout << std::endl;
std::map<ItemKey, size_t> counter2;
counter2[{5.0, 10.0}] += 1;
counter2[{3.0, 10.0}] += 1;
counter2[{7.0, 10.0}] += 1;
for (auto &itempair : counter2) {
std::cout << "simple_test_map()::counter2: ("
<< itempair.first.m_t << ", "
<< itempair.first.m_f << ") - "
<< itempair.second << "\n";
}
std::cout << std::endl;
}
This outputs:
simple_test_map()::counter1: (3, 10) - 2
simple_test_map()::counter1: (7, 10) - 1
simple_test_map()::counter2: (5, 10) - 3
And for the multiset variant:
void simple_test_multiset() {
std::multiset<ItemKey> counter1 {{3.0, 10.0}, {5.0, 10.0}, {7.0, 10.0}};
for (auto &item : counter1) {
std::cout << "simple_test_multiset()::counter1: ("
<< item.m_t << ", "
<< item.m_f << ")\n";
}
std::cout << std::endl;
std::multiset<ItemKey> counter2 {{5.0, 10.0}, {3.0, 10.0}, {7.0, 10.0}};
for (auto &item : counter2) {
std::cout << "simple_test_multiset()::counter2: ("
<< item.m_t << ", "
<< item.m_f << ")\n";
}
std::cout << std::endl;
std::cout << "simple_test_multiset()::counter2.size() = "
<< counter2.size() << std::endl;
for (auto &item : counter1) {
std::cout << "simple_test_multiset()::counter2.count({"
<< item.m_t << ", "
<< item.m_f << "}) = "
<< counter1.count(item) << std::endl;
}
std::cout << std::endl;
}
This outputs
simple_test_multiset()::counter1: (3, 10)
simple_test_multiset()::counter1: (5, 10)
simple_test_multiset()::counter1: (7, 10)
simple_test_multiset()::counter2: (5, 10)
simple_test_multiset()::counter2: (3, 10)
simple_test_multiset()::counter2: (7, 10)
simple_test_multiset()::counter2.count({3, 10}) = 2
simple_test_multiset()::counter2.count({5, 10}) = 3
simple_test_multiset()::counter2.count({7, 10}) = 2
simple_test_multiset()::counter2.size() = 3
Note that count() here returns the number of elements within the multiset that are considered equal to the ItemKey passed in. This may be advantageous for situations where you want to ask "how many of my points are within my tolerance of a new point?"
Good luck!

multimap accumulate values

I have a multimap defined by
typedef std::pair<int, int> comp_buf_pair; //pair<comp_t, dij>
typedef std::pair<int, comp_buf_pair> node_buf_pair;
typedef std::multimap<int, comp_buf_pair> buf_map; //key=PE, value = pair<comp_t, dij>
typedef buf_map::iterator It_buf;
int summ (int x, int y) {return x+y;}
int total_buf_size = 0;
std::cout << "\nUpdated buffer values" << std::endl;
for(It_buf it = bufsz_map.begin(); it!= bufsz_map.end(); ++it)
{
comp_buf_pair it1 = it->second;
// max buffer size will be summ(it1.second)
//total_buf_size = std::accumulate(bufsz_map.begin(), bufsz_map.end(), &summ); //error??
std::cout << "Total buffers required for this config = " << total_buf_size << std::endl;
std::cout << it->first << " : " << it1.first << " : " << it1.second << std::endl;
}
I would like to sum all the values pointed by it1.second
How can the std::accumulate function access the second iterator values?
Your issue is with the summ function, you actually need something better than that to be able to handle 2 mismatched types.
If you're lucky, this could work:
int summ(int x, buf_map::value_type const& v) { return x + v.second; }
If you're unlucky (depending on how accumulate is implemented), you could always:
struct Summer
{
typedef buf_map::value_type const& s_type;
int operator()(int x, s_type v) const { return x + v.second.first; }
int operator()(s_type v, int x) const { return x + v.second.first; }
};
And then use:
int result = std::accumulate(map.begin(), map.end(), 0, Summer());
I think you'll just need to change your summ function to take the map value_type instead. This is totally untested but it should give the idea.
int summ (int x, const buf_map::value_type& y)
{
return x + y.second;
}
And call it:
total_buf_size = std::accumulate(bufsz_map.begin(), bufsz_map.end(), 0, &summ);
Why do you mess about with pairs containing pairs? It is too complicated and you'll wind up making errors. Why not define a struct?
Accumulate is a generalization of summation: it computes the sum (or some other binary operation) of init and all of the elements in the range [first, last).
... The result is first initialized to init. Then, for each iterator i in [first, last), in order from beginning to end, it is updated by result = result + *i (in the first version) or result = binary_op(result, *i) (in the second version).
Sgi.com
Your attempt was neither first or second version, you missed the init part
total_buf_size = std::accumulate(bufsz_map.begin(), bufsz_map.end(), 0, &summ);