Priority Queue With a Custom Comparator No Matching Constructor - c++

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.

Related

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;
});

How to use boost::assign with custom containers that extend STL containers?

What do I have to do to make custom classes (that inherit from STL containers like std::vector or std::map) work with the boost::assign list_of() or map_list_of() initializer functions?
Background
I want to easily initialize a containers with a list of values. C++11 introduced initializer lists however I am stuck with C++03 so I cannot use C++11 initializer lists.
As a workaround I found the boost:assign library that provides functions like list_of() and map_list_of(). This works quite well for the STL containers like std::vector and std::map. However if I create my own containers by e.g. by extending std::vector I get compilation errors.
Example
Here is a small example
#include "boost/assign/list_of.hpp"
using namespace boost::assign;
#include <vector>
struct SpecialVector : public std::vector<int>{
foo(){/* adds functionality */}
};
int main(){
std::vector<int> v = list_of(1)(2)(3); // list_of() works well for STL containers
// The following works but requires adding items one-by-one with push_back
SpecialVector u;
u.push_back(1);
u.push_back(2);
u.push_back(3);
// The following fails when attempting to compile
SpecialVector u2 = list_of(1)(2)(3);
}
Attempting to compile the example gives me the following error:
In file included from assign_inherited.cpp:1:0:
../../../lib/boost/assign/list_of.hpp: In instantiation of 'Container boost::assign_detail::converter<DerivedTAssign, Iterator>::convert(const Container*, boost::assign_detail::default_type_tag) const [with Container = SpecialVector; DerivedTAssign = boost::assign_detail::generic_list<int>; Iterator = std::_Deque_iterator<int, int&, int*>]':
../../../lib/boost/assign/list_of.hpp:142:38: required from 'Container boost::assign_detail::converter<DerivedTAssign, Iterator>::convert_to_container() const [with Container = SpecialVector; DerivedTAssign = boost::assign_detail::generic_list<int>; Iterator = std::_Deque_iterator<int, int&, int*>]'
../../../lib/boost/assign/list_of.hpp:436:81: required from 'boost::assign_detail::generic_list<T>::operator Container() const [with Container = SpecialVector; T = int]'
assign_inherited.cpp:19:39: required from here
../../../lib/boost/assign/list_of.hpp:163:20: error: no matching function for call to 'SpecialVector::SpecialVector(boost::assign_detail::converter<boost::assign_detail::generic_list<int>, std::_Deque_iterator<int, int&, int*> >::iterator, boost::assign_detail::converter<boost::assign_detail::generic_list<int>, std::_Deque_iterator<int, int&, int*> >::iterator)'
return Container( begin(), end() );
^~~~~~~~~~~~~~~~~~~~~~~~~~~
assign_inherited.cpp:5:8: note: candidate: SpecialVector::SpecialVector()
struct SpecialVector : public std::vector<int>{
^~~~~~~~~~~~~
assign_inherited.cpp:5:8: note: candidate expects 0 arguments, 2 provided
assign_inherited.cpp:5:8: note: candidate: SpecialVector::SpecialVector(const SpecialVector&)
assign_inherited.cpp:5:8: note: candidate expects 1 argument, 2 provided
I already checked the documentation of the boost::assign library. I found the section Extending the library, however if I understand it correctly, this section deals with adding custom classes as items in the list, not with generating an initializer for a custom class. Or did I understand this wrong?
Like you said, you need to allow the construction from base types:
Live On Coliru
#include "boost/assign/list_of.hpp"
using namespace boost::assign;
#include <vector>
struct SpecialVector : std::vector<int>{
typedef std::vector<int> base;
void foo(){/* adds functionality */}
SpecialVector() : base() {}
template <typename T> explicit SpecialVector(T const& t) : base(t) {}
template <typename T, typename U> SpecialVector(T const& t, U const& u) : base(t, u) {}
template <typename T, typename U, typename V> SpecialVector(T const& t, U const& u, V const& v) : base(t, u, v) {}
};
int main(){
std::vector<int> v = list_of(1)(2)(3); // list_of() works well for STL containers
// The following works but requires adding items one-by-one with push_back
SpecialVector u;
u.push_back(1);
u.push_back(2);
u.push_back(3);
// The following fails when attempting to compile
SpecialVector u2 = list_of(1)(2)(3);
}

Why does this custom comparator fail in construcing std::priority_queue while it works for std::sort?

A comparator comp was defined as below. It works fine with std::sort, but fails to compile in the constructor of std::priority_queue. What is the problem? Thanks.
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
bool comp(int a, int b) { return a > b; }
int main()
{
vector<int> vec = {4, 2, 1, 3};
sort(vec.begin(), vec.end(), comp); // OK
priority_queue<int> q1(less<int>(), vec); // OK
priority_queue<int> q2(comp, vec); // Fail
return 0;
}
Error message:
error: no matching function for call to 'std::priority_queue<int>::priority_queue(bool (&)(int, int), std::vector<int>&)'
priority_queue<int> q2(comp, vec);
^
The type of the default comparator of std::priority_queue is std::less<T> where T is the value type. You are passing something of type bool(*)(int, int) instead. std::sort() being a function can deduce the comparator's type. Class types can't deduce their template arguments (yet - there us discussion in the C++ committee that a future version may have class templates whose template arguments can be deduced.
You can use
std::priority_queue<int, std::vector<int>, bool(*)(int, int)> q(comp);
or, avoiding a hard-to-inline function pointer:
std::priority_queue<int, std::vector<int>, std::greater<int> > q;

Boost sub_range with for_each; why am I getting a const-reference?

On OS X Mavericks, using boost 1.55.0 and clang-500.2.79 (based on LLVM 3.3svn), I'm trying to iterate over a sub-range in a std::map using boost::for_each and boost:sub_range. In my function-object, I expect to receive a std::pair &. Instead, I seem to receive a const std::pair &. Why?
#include <map>
#include <boost/range/algorithm.hpp>
#include <boost/range/sub_range.hpp>
using std::map;
using std::begin;
using std::end;
using std::pair;
using boost::for_each;
using boost::sub_range;
int main()
{
map<int, int> myMap;
sub_range<decltype(myMap)> s{
begin(myMap),
end(myMap)
};
auto f1 = [&](const pair<int, int> &) {
};
for_each(s, f1); // Compiles fine
auto f2 = [&](pair<int, int> &) {
};
for_each(s, f2); // Fails to compile
}
/Users/ambarish> clang++ main.cxx
In file included from main.cxx:1:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/map:371:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/__tree:18:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/algorithm:793:9: error: no matching function for call to object of type '<lambda at main.cxx:24:15>'
__f(*__first);
^~~
/usr/local/include/boost/range/algorithm/for_each.hpp:80:12: note: in instantiation of function template specialization
'std::__1::for_each<std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int, int>, std::__1::__tree_node<std::__1::pair<int, int>, void *> *, long> >, <lambda at main.cxx:24:15> >'
requested here
return std::for_each<
^
main.cxx:26:5: note: (skipping 1 context in backtrace; use -ftemplate-backtrace-limit=0 to see all)
for_each(s, f2);
^
main.cxx:24:15: note: candidate function not viable: no known conversion from 'value_type' (aka 'pair<__key_type, __mapped_type>') to 'pair<int, int> &' for 1st argument
auto f2 = [&](pair<int, int> &) {
^
1 error generated.
It doesn't compile because the value type of std::map<int, int> is not std::pair<int,int> but std::pair<const int, int>.
The reason first one (f1) compiles is because std::pair has got this constructor:
template< class U1, class U2 >
pair( pair<U1, U2>&& p );
and because f1 takes the argument by const reference. Now there's a suitable conversion which produces a temporary that can bind to a const reference easily.
Fix:
auto f2 = [&](pair<const int, int> &) { };
// or
auto f2 = [&](pair<int, int>) { };
The code appears to be using std::for_each in the boost library implementation, and C++11 style standard lambdas are being passed to the for_each function at any rate. It really depends on how the library passes the container's iterators to the std::for_each

undefined reference to std::hash<string>

I'm trying to write a simple factory function for std::unordered_map. The function takes in an iterable which has a begin and end method and whose value_type is a std::pair. The following is the code that I come up with.
#include <string>
#include <unordered_map>
#include <cassert>
#include <algorithm>
template <class Iterable>
std::unordered_map<typename Iterable::value_type::first_type,
typename Iterable::value_type::second_type>
make_unordered_map(Iterable const &iter)
{
return std::unordered_map<typename Iterable::value_type::first_type,
typename Iterable::value_type::second_type>(
iter.begin(), iter.end());
}
int main()
{
std::unordered_map<std::string, int> map =
{{"a", 0}, {"b", 1}, {"c", 2}};
auto result = make_unordered_map(map);
assert(std::equal(result.begin(), result.end(), map.begin()));
return 0;
}
However, I get a long list of linker error, and it basically asks for the std::hash class specialized for std::string.
undefined reference to `std::hash<std::basic_string<char, std::char_traits<char>,
std::allocator<char> > const>::operator()(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >) const'
I'm using GCC 4.6.1, with the -std=c++0x option. I'm pretty sure std::hash<std::string> is defined in basic_string.h, which is included in <string>.
Any idea how this happens?
You're getting your type deduction mixed up. It's important to remove qualifiers from the deduced types, so declare your template like this:
#include <functional>
template <class Iterable>
std::unordered_map<typename std::decay<typename Iterable::value_type::first_type>::type,
typename Iterable::value_type::second_type>
make_unordered_map(Iterable const &iter)
{
return std::unordered_map<
typename std::decay<typename Iterable::value_type::first_type>::type,
typename Iterable::value_type::second_type>(iter.begin(), iter.end());
}
Without this, you end up with const std::string as the key type, for which there is no specialization of std::hash.
Check out how real-world library code is written (e.g. the GCC standard library implementation) to see how to handle template types judiciously.
(By the way, std::equal is probably not the best tool for unordered maps.)