Sorting a vector via a predicate: compilation differences across different OSs!

Ah hello.

Please consider the following code, which looks simple but is hiding some fiendish problems:
#include <vector>
#include <algorithm>
#include <iostream>
#ifndef _WIN32
#include <strings.h>
#define stricmp strcasecmp
#endif

using namespace std;

class CParameter
{
public:
	CParameter(const char* pszName, int iLinenum) : m_strName(pszName), m_iLinenum(iLinenum) {}
	~CParameter() {}
	std::string& GetName() { return m_strName; }
	int GetLineNumber() const { return m_iLinenum; }
protected:
	int m_iLinenum;
	std::string m_strName;
};

struct sort_predicate
{

	//bool operator() (CParameter* const lhs, CParameter* const rhs)
	//{
	//	int nRet = stricmp(lhs->GetName().c_str(), rhs->GetName().c_str());
	//	return (nRet == 0) ? (lhs->GetLineNumber() < rhs->GetLineNumber()) : (nRet < 0);
	//}
	bool operator() (CParameter*& lhs, CParameter*& rhs)
	{
		int nRet = stricmp(lhs->GetName().c_str(), rhs->GetName().c_str());
		return (nRet == 0) ? (lhs->GetLineNumber() < rhs->GetLineNumber()) : (nRet < 0);
	}
	bool operator() (const std::string& lhs, CParameter*& rhs)
	{
		return stricmp(lhs.c_str(), rhs->GetName().c_str())< 0;
	}

	bool operator() (CParameter*& lhs, const std::string& rhs)
	{
		return stricmp(lhs->GetName().c_str(), rhs.c_str()) < 0;
	}        
};

int main()
{

	std::vector<CParameter*> m_parameters, storage;
	std::string strParamName = "Second";
	m_parameters.push_back(new CParameter("C", 1));	
	m_parameters.push_back(new CParameter("B", 4));
	m_parameters.push_back(new CParameter("D", 2));
	m_parameters.push_back(new CParameter("A", 5));
	m_parameters.push_back(new CParameter("A", 3));

	std::sort(m_parameters.begin(), m_parameters.end(), sort_predicate());

	cout << "First: " << m_parameters[0]->GetName().c_str() << " at line number " << m_parameters[0]->GetLineNumber() << endl;
	cout << "Second: " << m_parameters[1]->GetName().c_str() << " at line number " << m_parameters[1]->GetLineNumber() << endl;
	
}

Open in new window


OK.  So I am trying to compile this on three platforms, Windows (via VS 2005) Solaris 10 (via CC) and Oracle Enterprise Linux (via g++).  All three give different results.

On Windows, the code above just works fine.

On Solaris, it complains

".../include/CC/Cstd/./algorithm", line 665: Error: Could not find a match for sort_predicate::operator()(CParameter*const, CParameter*const) needed in std::__median<CParameter*, sort_predicate>(CParameter*const&, CParameter*const&, CParameter*const&, sort_predicate).
".../include/CC/Cstd/./algorithm", line 796:     Where: While instantiating "std::__introsort_loop<CParameter**, int, sort_predicate>(CParameter**, CParameter**, int, sort_predicate)".


On OEL, it complains:

/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_algo.h:124: error: no match for call to '(sort_predicate) (CParameter* const&, CParameter* const&)'
foo2.cpp:31: note: candidates are: bool sort_predicate::operator()(CParameter*&, CParameter*&)
foo2.cpp:36: note:                 bool sort_predicate::operator()(const std::string&, CParameter*&)
foo2.cpp:41: note:                 bool sort_predicate::operator()(CParameter*&, const std::string&)


So I added in my bool operator() (CParameter* const lhs, CParameter* const rhs) function (commented above), and commented my bool operator() (CParameter*& lhs, CParameter*& rhs) one (to avoid ambiguity errors between the two).  The code then worked on Solaris and OEL.

Poking at this a little more, I commented out the definitions of

bool operator() (const std::string& lhs, CParameter*& rhs)
bool operator() (CParameter*& lhs, const std::string& rhs)

...and added back my original

bool operator() (CParameter*& lhs, CParameter*& rhs)

and the code then started working Solaris but still failed on OEL, complaining the same as before:

/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_algo.h:124: error: no match for call to '(sort_predicate) (CParameter* const&, CParameter* const&)'
candidates are: bool sort_predicate::operator()(CParameter*&, CParameter*&)


Right.  So, can someone explain

1) Why the differences in initial compile behaviour as above - is it as simple as putting it down to different implementations of the STL?
2) Why did removing the two operators that took a string and a CParameter help on Solaris?  (Or is it again STL implementation specific).
3) (Most importantly) I always thought that the signature of an operator() in a sort predicate like this should be (TYPE&, TYPE&) where TYPE is whatever is stored in the container: Windows seems to agree with me on this (but from past quesions here I know many people don't trust Windows...), so can someon tell me what is the standard signature for operator() that should be used?

Many thanks in advance :)
LVL 19
mrwad99Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

jkrCommented:
Try

#include <vector>
#include <algorithm>
#include <iostream>
#ifndef _WIN32
#include <strings.h>
#define stricmp strcasecmp
#endif

using namespace std;

class CParameter
{
public:
	CParameter(const char* pszName, int iLinenum) : m_strName(pszName), m_iLinenum(iLinenum) {}
	~CParameter() {}
	const std::string& GetName() const { return m_strName; }
	int GetLineNumber() const { return m_iLinenum; }
protected:
	int m_iLinenum;
	std::string m_strName;
};

struct sort_predicate
{

	bool operator() (const CParameter* const& lhs, const CParameter* const& rhs)
	{
		int nRet = stricmp(lhs->GetName().c_str(), rhs->GetName().c_str());
		return (nRet == 0) ? (lhs->GetLineNumber() < rhs->GetLineNumber()) : (nRet < 0);
	}
};

int main()
{

	std::vector<CParameter*> m_parameters, storage;
	std::string strParamName = "Second";
	m_parameters.push_back(new CParameter("C", 1));	
	m_parameters.push_back(new CParameter("B", 4));
	m_parameters.push_back(new CParameter("D", 2));
	m_parameters.push_back(new CParameter("A", 5));
	m_parameters.push_back(new CParameter("A", 3));

	std::sort(m_parameters.begin(), m_parameters.end(), sort_predicate());

	cout << "First: " << m_parameters[0]->GetName().c_str() << " at line number " << m_parameters[0]->GetLineNumber() << endl;
	cout << "Second: " << m_parameters[1]->GetName().c_str() << " at line number " << m_parameters[1]->GetLineNumber() << endl;
	
}
                                  

Open in new window


That works for me with both VC++ and g++. I basically gave the compiler what it was asking for in

/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_algo.h:124: error: no match for call to '(sort_predicate) (CParameter* const&, CParameter* const&)'
candidates are: bool sort_predicate::operator()(CParameter*&, CParameter*&)


and adjusted the constness of your class' members.
0
mrwad99Author Commented:
Thanks; I am aware that I could make the code work by changing it in that way, but it concerned me since I cannot find anywhere that describes what the const-ness of the arguments to operator() must be: I looked at the definition of sort() at http://www.cplusplus.com/reference/algorithm/sort/?kw=sort, and it says that regarding the comparison method used, "The function shall not modify any of its arguments", yet it does not enforce that they be const.

Is there any documentation that describes this or is it just a case of compiling the code and seeing what different platforms require?
0
jkrCommented:
It's not the platforms, it is the way STL is implemented. As a rule of thumb, since a predicate never is to change any of the data it is passed, make it as const as it should be. For some reason, VC++ seems to be OK with

bool operator() (const CParameter*& lhs, const CParameter*& rhs);

Open in new window


(which I'd have chosen first as well), whereas g++ wants a

bool operator() (const CParameter* const& lhs, const CParameter* const& rhs);

Open in new window


The implication here is that you are using pointers in your vector (BTW, do you really have to?), which maked g++ not only demand the constness of the references, but of the pointers behind these as well.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
mrwad99Author Commented:
As has always been the case over the many years you have helped me, thanks :)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.