I am trying to store email addresses written in a file and sort them by hits in the file.
I am storing the email addresses and the number of hits in a class called emailAddress.
I am managing the members of that class in a Deque in another class called AddressManager.
The sort function I am trying to use is the sort from the algorithm library. Like this.
[emailAddress.released() returns the number of hits. addressQueue is my emailAddress Deque]
bool AddressManager::swapTest(const emailAddress& address1, const emailAddress& address2)
{
cout<<"Comparing: "<<address1.released()<<" to "<<address2.released()<<endl;
return address1.released()>address2.released();
}
void AddressManager::sortAddresses()
{
sort(addressQueue.begin(),addressQueue.end(),
swapTest);
}
When I compile I get this error:
1>c:\workspace\addressmanager.cpp(36): error C3867: 'AddressManager::swapTest': function call missing argument list; use '&AddressManager::swapTest' to create a pointer to member
1>c:\workspace\addressmanager.cpp(36): error C2780: 'void std::sort(_RanIt,_RanIt)' : expects 2 arguments - 3 provided
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm(3639) : see declaration of 'std::sort'
Can I pass swapTest to sort, or does it need to be defined outside of AddressManager somehow?
Or could someone suggest a way to implement my own sort in AddressManager and not use the library version at all?
Thanks,
ANkh
Just define an
struct EmailSorter
{
bool EmailSorter::operator ()(const emailAddress &a, const emailAddress &b) {
return a.released()>b.released();
}
};
EmailSorter es;
std::sort( v.begin() , v.end() , es );
and pass it to std::sort
Or make swapTest a static function and do
std::sort( v.begin() , v.end() , &AddressManager::swapTest );
Either make swapTest a static function, or use a lambda:
sort(addressQueue.begin(),addressQueue.end(),
[](const emailAddress& address1, const emailAddress& address2)
{ return address1.released() > address2.released(); }
);
Related
I'm wracking my brain here for several hours, but I still don't understand why I'm getting an error when I'm trying to run this code.
After some time I managed to narrow it down to the expression:
pastryPrice()
which causes the problem - as you can see, I'm trying to build numerous comparators for one template function of sorting
struct dialingAreaComp{
inline bool operator()(const Deliver *d1, const Deliver *d2)const {
return d1->getDialingArea() < d2->getDialingArea();
}
};
struct pastryPrice {
inline bool operator()(const Pastry *p1, const Pastry *p2)const {
return p1->getPrice() < p2->getPrice();
}
};
template<class T>
void sortCollection(T& collection)
{
if ( typeid (collection) == typeid(vector <Deliver*>))
{
sort(collection.begin(), collection.end(), dialingAreaComp());
printCollection(collection);
}
else if (typeid (collection) == typeid(vector <Pastry*>))
{
sort(collection.begin(), collection.end(), pastryPrice());
printCollection(collection);
}
else { cout << "WRONG!"; }
}
I'm getting five errors, all the same:
Severity Code Description Project File Line Suppression State
Error C2664 'bool Bakery::pastryPrice::operator ()(const Pastry *,const Pastry *) const': cannot convert argument 1 from 'Deliver *' to 'const Pastry *' Bakery c:\program files (x86)\microsoft visual studio 14.0\vc\include\xutility 809
And one more:
Severity Code Description Project File Line Suppression State
Error C2056 illegal expression Bakery c:\program files (x86)\microsoft visual studio 14.0\vc\include\xutility 809
When I take off the expression I wrote above, the code works just fine - why can't I pass two different comparators to one template function?
Now:
C2264 is a Compiler Error that occurs when one tries to pass a function a parameter of an incompatible type.
But the Deliver function works and when I took off the Deliver comparator the Pastry compiled as well... so what is the incompatible type?
Your problem is both branches are compiled regardless of which one is taken.
I would approach this differently.
template<class A, class B>
struct overload_t:A,B{
using A::operator();
using B::operator();
overload_t(A a, B b):A(std::move(a)), B(std::move(b)){}
};
template<class A, class B>
overload_t<A,B> overload( A a, B b ){
return {std::move(a),std::move(b)};
}
This lets us overload two function objects or lambdas. (Perfect forwarding could be added, as can varargs..., but I kept it simple).
Now we simply:
auto comp=overload(dialingAreaComp{}, pastryPrice{});
using std::begin; using std::end;
std::sort( begin(collection), end(collection), comp );
and the compiler chooses the correct comparison function for us. Also flat array support while I was in there.
And stop using using namespace std;.
What the above code does is fuze your two function object tyoes into one. The using A::operator() and using B::operator() moves both () into the same class and tells C++ to pick between them when invoked using the usual method call overloading rules. The rest of the code is glue to deduce the types being overloaded and move-construct them.
sort calls () with objects of the compile-time determined type based on the type of the container. Overload resolution (within sort at the point of call) then picks the right body to compare at compile time.
Thus technique can be extended with support for more than 2 overloads, function pointers, and forwarding references. In C++17 some work can be done to have the overload type deduce its parent types, removing the need for the factory function.
You get an error because the templated function is evaluated at compile time, and one of the function calls will never match. Instead of the template use simple function overloads:
void sortCollection(vector <Deliver*>& collection)
{
sort(collection.begin(), collection.end(), dialingAreaComp());
printCollection(collection);
}
void sortCollection(vector <Pastry*>& collection)
{
sort(collection.begin(), collection.end(), pastryPrice());
printCollection(collection);
}
I am a beginner in C++ and I don't know nor can find the way to address my problem.
I'm trying to sort my vector in an unusual way and fail to do so.
pointsToVisit - list of Point objects that can have their start time and end time.
visitedPoints - list of indexes of Point objects from pointsToVisit vector
I'd like to sort my visitedPoints vector by values of respective Points
BeeHive
std::vector<Point> pointsToVisit;
std::vector<Route> routes;
Route
std::vector<int> visitedPoints;
My attemp is below:
bool BeeHive::isPointsVisitStartPrior (int i, int j) { return (pointsToVisit.at(i).startTime<pointsToVisit.at(j).startTime); }
Route BeeHive::sortRouteByStartTime(int routeIndex){
Route route2 = Route();
route2.setStartTime(routes.at(routeIndex).getStartTime());
route2.setVisitedPoints(routes.at(routeIndex).getVisitedPoints());
std::sort(route2.getVisitedPoints().begin()+1, route2.getVisitedPoints().end(), isPointsVisitStartPrior);
evaluateRoute(route2);
return route2;
}
And I get such errors:
Error 5 error C3867: 'BeeHive::isPointsVisitStartPrior': function call missing argument list; use '&BeeHive::isPointsVisitStartPrior' to create a pointer to member c:\vrp projekt\vrp\vrp\beehive.cpp 193 1 VRP
Error 6 error C2780: 'void std::sort(_RanIt,_RanIt)' : expects 2 arguments - 3 provided c:\vrp projekt\vrp\vrp\beehive.cpp 193 1 VRP
Example by which I tried to do my work is under this address: http://www.cplusplus.com/reference/algorithm/sort/
I'll be thankful for any help received.
Might it be possible, that creating bubble sort for my own purposes will substitute std::sort() decently?
isPointsVisitStartPrior is a member function, this cannot be used directly in sort. You have to either use a global function or a function-object.
If you have access to C++ 11 features, you can use a lambda:
std::sort(route2.getVisitedPoints().begin()+1, route2.getVisitedPoints().end(),
[&](int i, int j){ return isPointsVisitStartPrior(i, j); });
You can also make a functor object with an operator(), something like
class VisitedPointsCompararer {
public:
VisitedPointsCompararer(const std::vector<Point>& pointsToVisit): pointsToVisit(pointsToVisit) {
}
bool operator() (int i, int j) {
return pointsToVisit.at(i).startTime < pointsToVisit.at(j).startTime;
}
...
private:
const std::vector<Point>& pointsToVisit;
}
isPointsVisitStartPrior(int, int) is a member function. A such, while it looks like it takes two arguments, it really implicitly takes three: it also needs a BeeHive* on which to operate (the this pointer).
What you need to do is "bind" the BeeHive* to the call:
using namespace std::placeholders;
std::sort(route2.getVisitedPoints().begin()+1,
route2.getVisitedPoints().end(),
std::bind(&BeeHive::isPointsVisitStartPrior, this, _1, _2)
// ^^^^^^^^^ without C++11, there's also boost::bind
);
That will save off this as the first argument in the call to the three-argument function, and forward the next two arguments you call it with into the next two slots.
This is logically equivalent to:
std::sort(route2.getVisitedPoints().begin()+1,
route2.getVisitedPoints().end(),
[this](int i, int j){ return isPointsVisitStartPrior(i, j); }
);
Though with the lambda, the compiler may be able to inline the call for you.
i got some issues trying to convert my map into a set
I got a "Chanson" object with this member data :
std::map<std::string,Artiste*> m_interpretes;
Here is how i add my *Artiste to my map :
void Chanson::addArtiste(Artiste* a) throw (ExceptionArtiste, ExceptionLangueIncompatible)
{
if(a!=NULL)
{
if(a->getLangue() == this->getLangue())
{
m_interpretes.insert(pair<string, Artiste*>(a->getNom(), a));
//m_interpretes[a->getNom()] = a;
}
else
{
throw ExceptionLangueIncompatible(a,this);
}
}
}
set<Artiste*> Chanson::getArtistes() const
{
//set<Artiste*> machin;
return set<Artiste*> (m_interpretes.begin(), m_interpretes.end());
}
i got this error due to this function :
Error C2664: 'std::pair<_Ty1,_Ty2> std::set<_Kty>::insert(Artiste *&&) : impossible de convertir le paramètre 1 de const std::pair<_Ty1,_Ty2> en 'Artiste *&&' c:\program files (x86)\microsoft visual studio 11.0\vc\include\set 179 1
Any idea how to fix it?
A map is an associative data structure, while a set only contains unordered collection of items, so adding a pair (key, value) is invalid for the latter and only holds for the former.
To make a set of keys from a map, you can do
std::set<Artiste*> tempSet;
std::transform(m_interpretes.cbegin(), m_interpretes.cend(),
std::inserter(tempSet, tempSet.begin()),
[](const std::pair<std::string, Artiste*>& key_value)
{ return key_value.second; });
return tempSet;
The std::set constructor you are trying to use will try to construct an element from everything the range you pass it:
return set<Artiste*> (m_interpretes.begin(), m_interpretes.end());
But the element type of that range is
std::pair<const std::string, Artiste*>
which is definitely not convertible to Artiste*, which is why you are getting that error about not being able to convert. You could just do it manually though:
std::set<Artiste*> s;
for (const auto& pair : m_interpretes) {
s.insert(pair.second);
}
The problem is here:
return set<Artiste*> (m_interpretes.begin(), m_interpretes.end());
If you have a look at the types you get from the map::begin() and map::end() functions you see that you get an iterator of std::pair<string, Artiste*>.
The problem is that the set::insert() function expects the iterators it is given to be of type Artiste*.
The simplest fix would be to create the set with a for loop, as shown in Barry's answer.
Currently I am trying to sort a vector of structs based on a specific field. I have set up a custom comparison function for the use of the sort function. However, i am getting some errors with it.
Code:
struct Play{
int min, down, yard, locat;
string Description, offname, defname;
double relevance;
};
bool customCompare(const Play &x, const Play &y)
{
return (x.relevance < y.relevance);
}
void printResults()
{
sort(vecData.begin(),vecData.end(), customCompare);
}`
Errors:
error C3867: 'List::customCompare': function call missing argument list; use '&List::customCompare' to create a pointer to member
error C2780: 'void std::sort(_RanIt,_RanIt)' : expects 2 arguments - 3 provided
a) Use sort function with lambda notation as below( if you are using c++11)
sort(vecData.begin(),vecData.end(), [](const Play &x, const Play &y){ return (x.relevance < y.relevance);});
Working code:
http://ideone.com/bDOrBV
b) Make comparator function as static
http://ideone.com/0HsaaH
Although this is an old question, I would like to note for the benefit of the future readers the possibility of directly sorting according to a specific field with the help of projections in the upcoming Ranges library in C++20:
ranges::sort(vecData, ranges::less, &Play::relevance);
This avoids the need of specifying two iterators or writing a custom comparison function or lambda.
static bool customCompare(const Play &x, const Play &y)
I have an instance method that populates a vector of strings. I am trying to find the one vector entry that contains a specific substring (for now, that substring is fixed - simple).
I have a .h:
namespace Data
{
namespace Shared
{
class Logger
{
public:
bool FindLogDirectoryPredicate(const string &str);
int GetLogDirectory(string logConfigFile, string& logDirectory);
...
}
}
}
and .cpp:
#include <algorithm>
#include <vector>
#include "Logger.h"
bool Logger::FindLogDirectoryPredicate(const string &str)
{
// Return false if string found.
return str.find("File=") > 0 ? false : true;
}
int Logger::GetLogDirectory(string logConfigFile, string& logDirectory)
{
vector<string> fileContents;
...
vector<string>::iterator result = find_if(fileContents.begin(), fileContents.end(), FindLogDirectoryPredicate);
...
}
Compiling this in Visual Studio 2010, I receive:
Error 7 error C3867: 'Data::Shared::Logger::FindLogDirectoryPredicate': function call missing argument list; use '&Data::Shared::Logger::FindLogDirectoryPredicate' to create a pointer to member Logger.cpp 317 1 Portability
Throwing an & in front of the function ref in the find_if call then results in:
Error 7 error C2276: '&' : illegal operation on bound member function expression Logger.cpp 317 1 Portability
I did try to put the predicate function outside the class, but that didn't seem to work - gave me a function not found error. Tried qualifying the predicate with the class name... that gave me a different error in algorithm (header):
Error 1 error C2064: term does not evaluate to a function taking 1 arguments c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\algorithm 83 1 Portability
The example I was following from here seems to indicate that this is relatively simple.... so what am I doing wrong?
The problem is that FindLogDirectoryPredicate is an instance method: it's not enough to specify its name, you somehow have to specify which object that method should be called on. Now the answer to this question is obvious to us (this), but not to the compiler.
The classic way to do this is with
find_if(fileContents.begin(),
fileContents.end(),
bind1st(mem_fun(&Logger::FindLogDirectoryPredicate), this));
What's going on here?
mem_fun "converts a member function to a function object". That is, it creates an instance of a type (what type exactly is unspecified, but we don't care) that exposes operator() (this is what we do care about!). This operator expects the first parameter to be a pointer to an instance of the type that defines the member function; here, that would be an instance of Logger.
bind1st then takes this function object that takes two parameters (first is the pointer to instance, second is the original const string & parameter) and returns a different function object that takes just one parameter (the const string &). The other parameter is fixed to the value of bind1st's second argument (this).
Alternatively, if you can make FindLogDirectoryPredicate static then there's no longer any need to specify which instance to call it on, so the problem will automatically go away.
Make the predicate static
class Logger
{
public:
static bool FindLogDirectoryPredicate(const string &str);
}
Or perhaps, use a lambda.
result = std::find_if(begin(), end(), [&this] (const std::string& s)
{ return FindLogDirectoryPredicate(s); } );
You can also use a std::mem_fun (and related <functional> stuff) if you must use C++98/C++03
result = std::find_if(begin(), end(),
std::bind1st(std::mem_fun(&Logger::FindLogDirectoryPredicate), this) );
Make your predicate a static class member.
static bool FindLogDirectoryPredicate(const string &str);