Lambda and map, param by reference - compile error - c++

I've tried to narrow down my problem to a minimal example:
#include <algorithm>
#include <map>
#include <string>
#include <vector>
int main()
{
std::vector<int> result;
std::map<std::string, std::pair<unsigned int, std::vector<int>>> other;
if (true)
{
std::for_each(other.begin(), other.end(),
[&](std::pair<std::string, std::pair<unsigned int, std::vector<int>>> & data)
{
result.insert(result.end(), data.second.second.begin(), data.second.second.end());
});
}
return 0;
}
I get a compiler error:
error C2664: 'void main::<lambda_1b93236899a42921c1aec8d5288e5b90>::operator ()(std::pair<std::string,std::pair<unsigned int,std::vector<int,std::allocator<_Ty>>>> &) const': cannot convert argument 1 from 'std::pair<const _Kty,_Ty>' to 'std::pair<std::string,std::pair<unsigned int,std::vector<int,std::allocator<_Ty>>>> &'
As far as I can tell the lambda parameter is indeed a reference to the type that is contained by the map which we are iterating through. That is the type I am supposed to be using, right?
If I remove the amperstand before data it compiles.
Why?
I do not want to pass each element by value, as those collections will contain a lot of data in my real program.
If I replace the lambda param with auto & it compiles, which leads me to believe the type in the lambda param does not match the type contained by the map, but it sure looks like it does to me. Also, why would the original compile without & if the type is wrong?
What am I missing/not understanding?

std::map<Key, T>'s value_type is std::pair<const Key, T>, not std::pair<Key, T>. The version without an ampersand makes a copy of each pair. Since you can copy const Key to Key everything is fine. Add a const to your lambda's parameter type.
#include <algorithm>
#include <map>
#include <string>
#include <vector>
int main()
{
std::vector<int> result;
std::map<std::string, std::pair<unsigned int, std::vector<int>>> other;
if (true)
{
std::for_each(other.begin(), other.end(),
[&](std::pair<const std::string, std::pair<unsigned int, std::vector<int>>> & data)
// Add this const ^^^^^
{
result.insert(result.end(), data.second.second.begin(), data.second.second.end());
});
}
return 0;
}

I've been strugglig with the very same problem today.
I solved that making the key value type in the std::pair const:
[&](std::pair<const std::string, std::pair<unsigned int, std::vector<int>>> & data)
// ^^^^^
After thinking about that a bit, it's quite logical:
You cannot change the key_value of a std::map's entry to something different. So if it's working with an auto loop and a reference it needs to be specified as const.

Related

Priority Queue With a Custom Comparator No Matching Constructor

I am trying to create a priority queue with a custom comparator but the following code gives me a compile error:
auto comparator = [](std::pair<std::vector<int>, File&> const &a, std::pair<std::vector<int>, File&> const &b) {
return a.first.front() > b.first.front();
};
std::priority_queue<std::pair<std::vector<uint64_t>, File&>,
std::vector<std::pair<std::vector<uint64_t>, File&>>,
decltype(comparator)> pq;
This is the error I am getting:
In template: no matching constructor for initialization of 'std::priority_queue<std::pair<std::vector, moderndbs::File &>, std::vector<std::pair<std::vector, moderndbs::File &>>, (lambda at
There's a mismatch in your question, between uint64_t and int. Assuming that this is squared away:
You need at least C++20 in order to compile the shown code. Prior to C++20, lambdas are not default-constructible, and you attempting to default-construct your std::priority_queue.
You need to explicitly pass the comparator to the constructor. Tested with gcc 10, with -std=c++17:
#include <queue>
#include <functional>
#include <vector>
#include <cstdint>
class File{};
void foo()
{
auto comparator = [](std::pair<std::vector<int>,
File &> const &a,
std::pair<std::vector<int>,
File &> const &b) {
return a.first.front() > b.first.front();
};
std::priority_queue<std::pair<std::vector<int>,
File &>,
std::vector<std::pair<std::vector<int>,
File &>
>,
decltype(comparator)> pq{comparator};
}
This fails with with a default-constructor pq. gcc 10 compiles the default-constructed pq with -std=c++20.
P.S.: consider replacing the File & with std::reference_wrapper, unless you really intend to do what the File & in std::pair will really do.

Difficulties with std::greater<int>() comparator with partial_copy_sort, "no matching function call.." on Mac OSX

Currently working with some ol' C++, but am having a touch of trouble with using the greater<int>() comparator for finding the top k keys with the max value in a map.
When compiling receiving the error:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/algorithm:5138:17: error: no matching function for call to object of type 'std::__1::greater<int>'
if (__comp(*__first, *__result_first))
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/algorithm:5160:12: note: in instantiation of function template specialization 'std::__1::__partial_sort_copy<std::__1::greater<int> &, std::__1::__hash_map_iterator<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >, int>, void *> *> >, std::__1::__wrap_iter<std::__1::pair<std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >, int> *> >' requested here
return __partial_sort_copy<_Comp_ref>(__first, __last, __result_first, __result_last, __comp);
^
Yikes that's ugly... Here's some context:
Context
I have an unordered_map<vector<string>,int>> construct which I am attempting to find the top k strings in my map which have the max int value.
#include <string>
#include <unordered_map>
#include <algorithm>
#include <functional>
#include <vector>
//...
unordered_map<vector<string>, int> database;
vector<pair <vector<string>, int> > top_k(3);
partial_sort_copy(my_map.begin(),
my_map.end(),
top_k.begin(),
top_k.end(),
greater<int>());
Not the best cpp programmer, would love to hear some suggestions you had to remedy this situation?
According to the documentation on cppreference, the comparator function requires a type signature like so:
bool cmp(const Type1 &a, const Type2 &b);
The types Type1 and Type2 must be such that an object of type RandomIt can be dereferenced and then implicitly converted to both of them. ​
The RandomIt iterators correspond to the top_k structure which when dereferenced has type pair <vector<string>, int>, while std::greater<int> has comparison function of bool operator()( const int& lhs, const int& rhs ). In other words, this does not work because pair <vector<string>, int> does not convert to int.
One solution is to provide your own comparator:
std::partial_sort_copy(my_map.begin(), my_map.end(), top_k.begin(), top_k.end(),
[](const pair<vector<string>, int>& lhs, const pair<vector<string>, int>& rhs) {
return lhs.second > rhs.second;
});

return reference to temporary when dereferencing map iterator

Consider this code
#include <iterator>
#include <vector>
const int& foo(const std::vector<int>& x,unsigned i) {
auto it = x.begin();
std::advance(it,i);
return *it;
}
Both clang and gcc emit no errors/warnings, but this:
#include <iterator>
#include <map>
const std::pair<int,int>& bar(const std::map<int,int>& x,unsigned i){
auto it = x.begin();
std::advance(it,i);
return *it;
}
compiled with clang and using -Werror results in :
<source>:14:12: error: returning reference to local temporary object [-Werror,-Wreturn-stack-address]
return *it;
^~~
and with gcc:
<source>: In function 'const std::pair<int, int>& bar(const std::map<int, int>&, unsigned int)':
<source>:14:13: error: returning reference to temporary [-Werror=return-local-addr]
return *it;
^~
What makes gcc and clang reject bar and why is foo fine?
The problem is that the value_type of std::map<int,int> is not std::pair<int,int>, but std::pair<const int,int>. Then for return *it;, a temporary std::pair<int,int> has to be created and returned. (std::pair<int,int> could be converted from std::pair<const int,int>.) The temporary will be destroyed immediately and left the returned reference dangled.
To sovle the issue you can change the return type to const std::pair<const int,int>& or const std::map<int,int>::value_type &.

Cast map<K, shared_ptr<V>> to map<K, shared_ptr<const V>>?

I'm storing an std::map of std::shared_ptr<V> as value.
From one functions, I want to return a reference to this map, but with std::shared_ptr<const V> as value type, is that possible ?
Here is what I want to achieve:
#include <map>
#include <memory>
struct A {
std::map<int, std::shared_ptr<int>> map;
const std::map<int, std::shared_ptr<const int>>& ret1() const {
return map;
}
};
But this code fails to compile:
error: invalid initialization of reference of type
'const std::map<int, std::shared_ptr<const int> >&'
from expression of type 'const std::map<int, std::shared_ptr<int> >'
Thanks
A type T and a type const T are related but ultimately different types. If you don't want the caller of ret1 to modify the values in your map, then either return a copy of the map, or accept a reference to a map as argument and copy the keys and values into that.

find() problems

I've got an error while using find() function. Here is the code:
#include <iostream>
#include <map>
#define N 100000
using namespace std;
int main (int argc, char * const argv[]) {
map<int,int> m;
for (int i=0; i<N; i++) m[i]=i;
find(m.begin(), m.end(), 5);
return 0;
}
I'm getting an compiller error:
error: no match for 'operator==' in '__first. __gnu_debug::_Safe_iterator<_Iterator, _Sequence>::operator* [with _Iterator = std::_Rb_tree_iterator<std::pair<const int, int> >, _Sequence = __gnu_debug_def::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >]() == __val'
Including 'algorithm' nothing changes. Compiling in VS2008 shows similar error.
I know about m.find(), but I realy need to use find() too.
Thanks a lot for your assistance!
P.S. Actualy, the task is to compare speed of m.find(5) and find(m.begin(), m.end(), 5), so I need to make both of them work properly.
begin() and end() on all STL containers provide access to elements of those collections. Type of those elements is known as value_type of the container. For std::map<Key, Value>, its value_type is std::pair<Key, Value>. Therefore, your find function is trying to find a pair<int, int> which is equal to 5. Since there's no operator== defined to compare pair<int, int> and int, you get the error.
The correct way to do this (so long as you want to avoid member find()) is to use std::find_if:
template <class First>
struct first_equal
{
const First value;
first_equal(const First& value)
: value(value)
{
}
template <class Second>
bool operator() (const std::pair<First, Second>& pair) const
{
return pair.first == value;
}
};
...
find_if(m.begin(), m.end(), first_equal<int>(5));
You could also overload operator== for pair and int to do what you want, but it's a very hackish way (because it will affect all your code, and because such a comparison has no meaning in general).
find() requires a parameter that can be compared to *iterator. For your map, this will be pair<int,int>. You'll need to create a dummy pair, plus a comparison functor to compare the pairs.
Just use m.find(5)