I have a struct with two fields :
struct road {
int from, len ;
};
For some reason, I need to be able to order my roads :
by ascending from in an array
by ascending len in a priority queue
I have thus included :
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
I have come across websites suggesting to overload the operator<, but because of the two possible orderings that just feels wrong and it would only solve one of the two.
By messing around with textbooks, I got this to work :
bool cmpFrom (const road & a, const road & b) {
return (a.from < b.from) ;
}
struct cmpLen {
bool operator () (const road & a, const road & b){
return (a.len < b.len) ;
}
};
To be used with :
std::sort(trips, trips + nbRoads, &cmpFrom) ;
std::priority_queue<road, std::vector<road>, cmpLen> pickRoad ;
Where trips is of course a road [].
It compiles perfectly (haven't tried running it, but it should be fine), but it seems weird to define two very similar comparators in two quite different manners, so isn't there a way to define both comparison methods the same way ?
Changing the definition of cmpFrom to
struct cmpFrom {
bool operator () (const road & a, const road & b){
return (a.from < b.from) ;
}
};
Gives
chantier.cpp: In function ‘int main()’:
chantier.cpp:38:48: error: expected primary-expression before ‘)’ token
std::sort(trips, trips + nbRoads, &cmpFrom) ;
Which I assume means "You gave me a type when I was expecting a reference".
While writing
bool cmpLen (const road & a, const road & b) {
return (a.len <= b.len) ;
}
Gives
chantier.cpp: In function ‘int main()’:
chantier.cpp:52:56: error: type/value mismatch at argument 3 in template parameter list for ‘template<class _Tp, class _Sequence, class _Compare> class std::priority_queue’
std::priority_queue<road, std::vector<road>, cmpLen> pickRoad ;
^
chantier.cpp:52:56: note: expected a type, got ‘cmpLen’
chantier.cpp:56:30: error: request for member ‘top’ in ‘pickRoad’, which is of non-class type ‘int’
...
Is there a way to make one of these comparison methods work for both containers ? Or is there perhaps a third way of doing this that could work with both ?
What if I had needed to use the same ordering with both containers ? Would that have required defining twice the same comparison method, but with one inside a struct ?
You almost have it. In std::sort you need an object that you can call operator() on. Using
bool cmpFrom (const road & a, const road & b) {
return (a.from < b.from) ;
}
std::sort(trips, trips + nbRoads, &cmpFrom);
works because a function pointer can be used like a function. When you change cmpFrom to
struct cmpFrom {
bool operator () (const road & a, const road & b){
return (a.from < b.from) ;
}
};
you can't use std::sort(trips, trips + nbRoads, &cmpFrom); anymore because you can't apply & to a type name. Instead what you need to do is get an object of cmpFrom and you do that like
std::sort(trips, trips + nbRoads, cmpFrom{});
now both the priority_queue and sort could use cmpFrom.
It's easier to define both as structures, because you can always create an object from a type and it will behave as expected, but getting a type from a function and having it act as a caller for the function is much more difficult.
You were in fact almost there with struct cmpFrom. However, you've correctly noted that std::sort expects a comparator object (such as a function), not a type. Of course, doing &cmpFrom where cmpFrom is a type is not valid C++. Instead, you need to create an object of that type; thanks to the operator() defined, the object will be callable and do what you want. So just call std::sort like this:
std::sort(trips, trips + nbRoads, cmpFrom{});
The std::sort function and std::priority_queue class template want two different things: sort wants a callable object, while priority_queue template wants a type, which allows creating objects.
Because of that, sort is more omnivorous than priority_queue - you can use it with either functions or functors. The only thing you need is to provide it with a real object (while currently in your code you are trying to take an address of a type, which makes no sense).
To fix it in your example just change the code to
std::sort(trips, trips + nbRoads, cmpFrom{});
Related
I am trying to create a set of function pointers in c++ but getting error while inserting / deleting elements from it.
#include<bits/stdc++.h>
using namespace std;
void func(int x) {
cout<<x;
}
int main() {
set <function<void (int)>> mine;
mine.insert(func);
return 0;
}
i am getting error
/usr/include/c++/6/bits/stl_function.h:386:20: error: no match for ‘operator<’ (operand types are ‘const std::function’ and ‘const std::function’).
I think that this problem is because of operator that will be used to compare set values , can someone suggest how to make this work ?
How can i write a comparator for function pointers in this case ?
If you want to store just function pointers, you don't need std::function:
using MyFunctionPointer = void(*)(int);
void func(int x);
std::set<MyFunctionPointer> orderedSet;
int main()
{
orderedSet.emplace(func);
}
Demo
This works because you can compare (for std::set) or hash (for std::unordered set) function pointer values. But comparing or hashing std::function instances is not implemented in the standard library, and there is no portable way to add that after the fact.
Edit: As pointed out by #HolyBlackCat, while the builtin operator< is not required to induce the required total order on function pointers, std::less (as used by std::set) is required to do so for any pointer.
I understand a set is ordered, thus adding an object without overloading the < operator doesn't allow to say which object is smaller to keep the container sorted. However, I don't understand why this isn't possible with an unordered_set.
If I try something like this:
#include <iostream>
#include <string
#include <unordered_set>
struct someType{
string name;
int code;
};
int main(){
std::unordered_set <someType> myset;
myset.insert({"aaa",123});
myset.insert({"bbb",321});
myset.insert({"ccc",213});
return 0;
}
I get a couple of errors like:
c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\hashtable_policy.h:1070: error: invalid use of incomplete type 'struct std::hash'
c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\functional_hash.h:58: error: declaration of 'struct std::hash'
error: no matching function for call to 'std::unordered_set::unordered_set()'
c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\hashtable_policy.h:1103: error: no match for call to '(const std::hash) (const someType&)'
c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\stl_function.h:208: error: no match for 'operator==' (operand types are 'const someType' and 'const someType')
Why is that and how can I fix it?
To use type in unordered_set or unordered_map you need hashing function for your type. For common types, like int or std::string - hashing function is provided by standard library. For your type, you can overload standard std::hash, like this:
namespace std {
template <> struct hash<someType> {
size_t operator()(const someType & x) const {
std::hash<std::string> h;
return h(x.name);
// or simply return x.code
// or do something more interesting,
// like xor'ing hashes from both members of struct
}
};
}
Another way is to provide your own type with overloaded operator() and put it as hash template argument in unordered_set, like this:
struct someTypeHasher {
size_t operator()(const someType& x) const {
return x.code;
}
};
std::unordered_set<someType, someTypeHasher> myset;
Good reading for theory about hash based containers is here
Also, do not forget, that you need to overload operator== for someType, without it - it will also not work.
As explained in the answer given by Starl1ght, you need to provide a hash function for someType. However, I would combine all members of your class by that hash function. Otherwise, you might get a lot of collisions, for example, if the same name occurs very often, but with different code values. For creating a hash function, you can make use of Boost, but you can also handcraft it.
Starl1ght also mentioned that you need to overload operator== for someType,
but you can also define a separate comparison function instead and provide it to the unordered_set. Moreover, you can use lambda expressions instead of defining the hash and comparison functions. If you put everything together, then your code could be written as follows:
auto hash = [](const someType& st){
return std::hash<std::string>()(st.name) * 31 + std::hash<int>()(st.code);
};
auto equal = [](const someType& st1, const someType& st2){
return st1.name == st2.name && st1.code == st2.code;
};
std::unordered_set<someType, decltype(hash), decltype(equal)> myset(8, hash, equal);
Code on Ideone
I have defined a class
class Rent
{
public:
int s_time, duration, price, e_time;
Rent(int s, int d, int p)
{
s_time = s;
duration = d;
price = p;
e_time = s + d;
}
bool operator<(Rent const &r1)
{
return e_time < r1.e_time;
}
};
Wished to sort it on basis of e_time, so I have defined < over Rent, however, I keep getting error
rent.cpp:38:12: error: no match for ‘operator+’ (operand types are ‘std::vector<Rent>’ and ‘int’)
sort(R, R+n);
^
when I tried sort(R, R+n);. R is a vector of type Rent and n is integer(size of the vector).
Besides the above, I tried these two ways but still failed!
sort(R, R + sizeof(R)/sizeof(R[0]));
sort(R.begin(), R.end());
I googled and got some solutions with lambdas but again the second parameter to sort() is of type int + custom_datatype.
Any help would be great.
sort(R, R+n);
sort(R, R + sizeof(R)/sizeof(R[0]));
will not work if R is of type std::vector<Rent>. There are two problems with those lines:
operator+() is not defined for std::vector.
The compiler expects the operator<() function to be a const member function.
You can fix the operator<() function by making it a const member function.
bool operator<(Rent const &r1) const
// ^^^^^
{
return e_time < r1.e_time;
}
That still does not resolve the first problem.
However you should be able to use:
sort(R.begin(), R.end());
after that.
In theory, you shouldn't have to make the operator<() function a non-const member function. Take a look at http://en.cppreference.com/w/cpp/algorithm/sort. See the description of the comp argument. It says:
The signature of the comparison function should be equivalent to the following:
bool cmp(const Type1 &a, const Type2 &b);
The signature does not need to have const &, but the function object must not modify the objects passed to it.
However, not all compilers adhere to that. They expect the signature of the function to be such that they can work with const objects.
According to error, R is a std::vector<Rent> but following code:
sort(R, R+n);
would work only for C style array. If you want generic code that works for both C array and std::vector write it as:
std::sort( std::begin(R), std::end(R) );
I have recently started trying to teach myself C++ and am new to the board. I have created a strut called permuted_index, and a vector of permuted_index objects called perm_index.
I have written an overloaded predicate called "stringCompare" to be used with the sort function to sort either a vector of strings or a vector of permuted_index objects. However, when I try and run my program get error C2914 - which as I understand it means that the sort function can not identify which of version of stringCompare to use.
I have been looking at this for days and am completely stumped! I can force my program to work by commenting out one version of the predicate, but I would really like to understand the underlying program and would appreciate any help. I have provided everything that I think will help anyone looking at this below but if you need any more information please let me know.
This is the permuted index strut;
struct permuted_index{
std::string word ;
std::vector<std::string>::size_type line ;
std::string::size_type position ;
std::vector<std::string> full_line ;
std::vector<std::string> rotated_line ;
std::string before_word ;
std::string after_word ; };
This is the overloaded predicate;
#include <string>
#include <vector>
#include "split.h"
#include "Permuted_index.h"
using std::string ;
using std::vector ;
bool stringCompare(const string& x, const string& y) {
vector<string> p ;
vector<string> q ;
p = split(x) ;
q = split(y) ;
return p[0] < q[0] ;
}
bool stringCompare(const permuted_index& x, const permuted_index& y){
return x.rotated_line[0] < y.rotated_line[0] ;
}
This is the split function called above;
vector<string> split(const string& s)
{
vector<string> ret ;
typedef string::size_type string_size ;
string_size i = 0 ;
while(i != s.size())
{
while(i != s.size() && isspace(s[i]))
{
++i ;
}
string_size j = i ;
while(j != s.size() && !isspace(s[j]))
{
++j ;
}
if(i != j)
{
ret.push_back(s.substr(i, j-i)) ;
i = j ;
}
}
return ret ;
}
The line within my main() program that is causing the error is;
sort(perm_index.begin(), perm_index.end(), stringCompare) ;
and the the exact error message is:
error C2914: 'std::sort' : cannot deduce template argument as function argument is ambiguous
For some reason, using a cast seems a little iffy to me. But this works:
void foo(std::vector<permuted_index> &pi)
{
using Compare = bool (*)(const permuted_index &, const permuted_index&);
Compare cmp = stringCompare;
std::sort(pi.begin(), pi.end(), cmp);
}
This declares a type alias, Compare.
Before C++ 11 this was done with what is called a typedef, as in:
typedef bool (*Compare)(const permuted_index &, const permuted_index &);
These both just say that Compare is a name for a function that takes two permuted_index objects and returns a bool. (Technically Compare is a pointer to a function but function names by themselves are also pointers.) The C/C++ syntax for function type names is not exactly the easiest thing to parse but if you read the typedef from the inside out it in sort of a left-right order (the "spiral" rule) it says Compare is a pointer (the asterisk) to a function taking two permuted_index references (the parentheses enclosing the reference declarations to the right) and returning bool) You can find a nice SO description of these topics here and there are various tutorials on function pointer syntax (evidence in and of itself that it isn't the simplest aspect of C/C++) around the web, such as this article that explains the spiral rule.
Anyway, Compare is an alias for a function of precisely the type that sort expects for a comparator when sorting permuted_index objects. We then declare a pointer instance, cmp that points to stringCompare. The compiler now knows precisely the type of cmp so there is no ambiguity as to which stringCompare we can assign to cmp.
As another aside, cmp is a pointer and you could actually write
Compare cmp = &stringCompare;
In C++, the name of a function by itself "decays" to a pointer to that function, so this is redundant and I left it out in my example.
Another approach is to an inline lambda. Lambdas are a new part of the language that allow you to declare a function in-place. This is a very useful syntax for things like comparators that are often only used once, when the algorithm is called. Like function pointers, the syntax takes a bit of getting used to. Here is a nice SO article on the subject. Basically the [] signals that this is a simple lambda (not a "closure" of any sort), this is followed by the arguments to the lambda, which is then followed by its body. (You can optionally declare the return type right after the arguments as
[](const permuted_index &, const permuted_index &) -> bool { ... }
but this isn't usually necessary as the compiler can infer the type from the function body.)
Anyway, as with the typedef above, this approach works because the lambda has declared what the arguments are so the right stringCompare will be chosen, and the type of the lambda is obvious to the compiler so there is no confusion over which function type to use to figure out the second template type in sort.
void bar(std::vector<permuted_index> &pi)
{
std::sort(pi.begin(), pi.end(),
[](const permuted_index &a, const permuted_index &b)
{ return stringCompare(a,b); });
}
I believe the issue is that the Compare template parameter is completely independent of the type being compared - there isn't currently a way for the library to specify that its type should have the signature of a comparison between the two value types being sorted on. (Though I'm not exactly sure why SFINAE doesn't remove the possibility of using the first overload.)
Try the following
sort(perm_index.begin(), perm_index.end(),
static_cast<bool(*)( const permuted_index &, const permuted_index & )>(stringCompare) );
Is it possible to have a set(or unordered_set ) of functions in c++ like this?
set<function<void(int)>> funcSet;
I got something like this
error: invalid operands to binary expression
('const std::__1::function<void (int)>' and 'const std::__1::function<void (int)>')
{return __x < __y;}
How can I compare two functions?
A set has the requirement that its elements may be ordered using <. So to put functions in a set, you have to define an ordering for functions first. For example, this comparison considers all functions with the same type as equal:
#include <set>
#include <functional>
using namespace std;
typedef function<void(int)> fun;
bool operator<(const fun& f1, const fun& f2) {
return f2.target_type().name() < f2.target_type().name();
}
int main() {
set<fun> fset;
}
Likewise, for an unordered_set, you'd have to define a specialization of std::hash<fun>.
Edit: I've borrowed the target idea from another solution to make the comparison well-defined.
Edit2: The most meaningful comparison for arbitrary functions would probably look like this:
struct fun_comp {
template<typename Fun1, typename Fun2>
bool operator()(const Fun1& f1, const Fun2& f2) {
const char* c1 = f1._M_functor._M_pod_data;
const char* c2 = f2._M_functor._M_pod_data;
size_t sz = sizeof(f1._M_functor._M_pod_data);
return lexicographical_compare(c1, c1+sz, c2, c2+sz);
}
};
This is, obviously, completely unportable, depends on libstdc++-internals and will only compile with -fno-access-control, so you probably shouldn't actually do it like this.
Would a std::vector<std::function<void(int)>> work?
If you want to order your functions it seems like you'd be the only one who knew the order anyway.
Perhaps creating an enum to index the std::vector would also be helpful for ordering?