Problems with binary predicates!

Please have a look at the following.
It seems, you forgot to post your problem.
I am using the STL function sort from the algorithm header file but I am having some issues with the binary predicate parameter that the function uses. Consider the skeletal illustrations below:


1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
   vector<extPersonType> addressBook;
      .
      .
      .    
   sort(addressBook.begin(), addressBook.end(), myComp);
      .
      .

   return 0;
}


Here is how I defined the binary predicate, myComp:

1
2
3
4
5
6
7
8
bool myComp (const extPersonType& p, const extPersonType& q)
{
    string u, v, w, x;

    p.getName(u, v);
    q.getName(w, x);
    return(v < x);
}


Class extPersonType is derived from class personType as briefly outlined below:

1
2
3
4
5
6
7
8
9
10
class extPersonType: public personType
{
    int phoneNo;
public:
    extPersonType(fName = "", lName = "", phNo = 0);
    void getName(string&, string&);
       .
       .
       .
};


1
2
3
4
5
6
7
8
9
10
11
class personType
{
    string firstName;
    string lastName;
public:
    personType(fName = "", lName = "");
    void getName(string&, string&);
       .
       .
       .
};


The program would not compile! I got these error messages for lines 5 & 6 of the myComp binary predicate (function) definition:

passing `const extPersonType' as `this' argument of `void extPersonType::getName(std::string&, std::string&)' discards qualifiers 
passing `const extPersonType' as `this' argument of `void extPersonType::getName(std::string&, std::string&)' discards qualifiers 



Here are my questions:

Qn 1: I know that making the getName functions in classes extPersonType and personType const would rectify the problem. However, can someone please break down what these error messages mean? How does 'this' argument come into play here? And what are the qualifiers being referred to?

Qn 2: Trying to circumvent the problem above, I removed the const and used ordinary reference parameters for myComp as shown below:

1
2
3
4
5
6
7
8
bool myComp (extPersonType& p, extPersonType& q)
{
    string u, v, w, x;

    p.getName(u, v);
    q.getName(w, x);
    return(v < x);
}

and then I got the error message below referencing line 7 in function main:

invalid initialization of reference of type 'extPersonType&' from expression of type 'const extPersonType' 

What does this mean?


Note: Passing the parameters by value into the myComp function works perfectly! I know it's not the best approach for the simple reason that the passed objects may be huge in size.
How does 'this' argument come into play here?
Membr functions can be represented as normal functions with hidden parameter as first argument.
So void Person::getName(string&, string&); is equivalent to void getName(Person* this, string&, string&), and call p.getName(u, v); is equivalent to getName(&p, u, v);.
const (volatile/&/&&) at the end of member functio
Error you are getting tells that trying to convert that hidden parameter from const extPersonType* this to extPersonType* this casts away const.

And what are the qualifiers being referred to?
cv-qualifiers.
http://en.cppreference.com/w/cpp/language/cv

and then I got the error message below referencing line 7 in function main:
This is strange. I cannot reproduce your problem. Can you post minimal example which shows this error?
Post it on http://rextester.com/runcode for Visual Studio and on http://coliru.stacked-crooked.com/ for gcc/clang
 
Edit: standard requires that comparison function should not modify object, that means it should not call any non-const member functions. So your comparison function is invalid if getName is not const.
Edit 2:
Standard wrote:
25.4                   [alg.sorting]  
1 All the operations in 25.4 have two versions: one that takes a function object of type Compare and one that uses an operator<.
2 Compare is a function object type (20.9). The return value of the function call operation applied to an object of type Compare, when contextually converted to bool (Clause 4), yields true if the first argument of the call is less than the second, and false otherwise. Compare comp is used throughout for algorithms assuming an ordering relation. It is assumed that comp will not apply any non-constant function through
the dereferenced iterator.
Last edited on
Start with making person::getName() const-correct.

Should I try to get things const correct “sooner” or “later”?

At the very, very, very beginning.

Back-patching const correctness results in a snowball effect: every const you add “over here” requires four more to be added “over there.”

Add const early and often.
https://isocpp.org/wiki/faq/const-correctness


Perhaps something along these lines:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <vector>
#include <string>
#include <utility>
#include <algorithm>

struct person
{
    explicit person( const std::string& first_name = {}, const std::string& last_name = {} )
         : first_name_(first_name), last_name_(last_name) {}

    virtual ~person() = default ; // almost always a good idea if the class is designed to be inherited from

    // favour value semantics over reference semantics
    std::string first_name() const { return first_name_ ; } // and make them const-correct
    std::string last_name() const { return last_name_ ; }

    private:
        std::string first_name_ ;
        std::string last_name_ ;
};

struct cmp_names
{
    static std::pair<std::string,std::string> names_as_pair( const person& p )
    { return { p.first_name(), p.last_name() } ; }

    bool operator() ( const person& a, const person& b ) const
    { return names_as_pair(a) < names_as_pair(b) ; }
};

struct person_with_phone : person
{
    using person::person ;
    explicit person_with_phone( int phone, const std::string& first_name = {}, const std::string& last_name = {} )
        : person(first_name,last_name), phone_(phone) {}

    int phone() const { return phone_ ; }
    void phone( int number ) { phone_ = number ; }

    private: int phone_ = 0 ;
};

int main()
{
   std::vector<person_with_phone> address_book ;
   // ...
   std::sort( address_book.begin(), address_book.end(), cmp_names() );
}

@ MiiniPaa:
Thx for your response. I stated in my OP that the code was "skeletal;" so obviously you would not be able to reproduce my problem. By the way, you wrote:
const (volatile/&/&&) at the end of member functio
It seems you did not finish your thought here. I would like to know what you were trying to convey.

@ JLBorges:
Thx for your reply. While your code may come across as an overkill for what I'm doing, I went through it hoping to learn from it (I'm still learning the C++ language). Hence, I have a few questions:

line 9 explicit person( const std::string& first_name = {}, const std::string& last_name = {} )

Are those two pairs of braces synonymous with the empty string ("") and, hence, this line indicates you are using default parameters?


line 34 using person::person ;

Why are you doing this if the struct already inherited struct person?


line 41 private: int phone_ = 0 ;

Is there any significance to specifically assigning this data member to zero?
so obviously you would not be able to reproduce my problem
I have striked this out. If you try to call nonconst member functions, anything can happen. I highlighted part where standard allows to reject such code. It is simply unspecified.

It seems you did not finish your thought here.
Argh! I was rearanging my answer for clarity and it seems that I have selected something I shouldn't have when copy-pasting.
It should be: const (volatile/&/&&) at the end of member function declaration are applied to that hidden this parameter.

Are those two pairs of braces synonymous with the empty string ("") and, hence, this line indicates you are using default parameters?
They are synonymous with "default-constructed string".

Why are you doing this if the struct already inherited struct person?
Be declaring constructor for person_with_phone we hide all base class constructors. We will need to either reimplement them (usually as delegating most work to base class constructor):
1
2
explicit person_with_phone( const std::string& first_name = {}, const std::string& last_name = {} ) : 
    person(first_name, last_name), phone(0) {}
or we can just inherit them allowing its use in derived class, what is done in this line.

Is there any significance to specifically assigning this data member to zero?
Yes. If we use inherited constructor, it would not initialize phone variable. So it can have any seemingly random value, which is not what we want. So by using brace-or-equal class member initializer we can be sure that any constructor which does not initialize phone itself will initialize it to 0.
> Are those two pairs of braces synonymous with the empty string ("")

void foo( const std::string& arg = {} ) ;

is equivalent to void foo( const std::string& arg = std::string{} ) ;
Value initialisation: http://en.cppreference.com/w/cpp/language/value_initialization
Uniform initialization: http://www.stroustrup.com/C++11FAQ.html#uniform-init

That it is a std::string that is being value-initialised is deduced from the context.
In this case, the reference arg is seated on an anonymous std::string that is default constructed.

1
2
3
int i{} ; // i is value initialised; i == 0
std::string str{} ; // str is value initialised; str.empty() == 0
std::vector<int> vec(5) ; // the elements are value initialised; vec contains { 0, 0, 0, 0, 0 } 


The reason why void foo( const std::string& arg = {} ) ; is to be favoured is that a compiler may generate more optimal code with it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <string>

namespace
{
  bool foo( const std::string& str = {} ) 
  { 
      return str.empty() ; 
  }

  bool bar( const std::string& str = "" ) 
  { 
      return str.empty() ; 
  }
}

bool call_foo() 
{ 
  return foo() ; 
}

bool call_bar() 
{ 
  return bar() ; 
}


clang++ (ok): http://goo.gl/y2VOo8
g++ (very smart, 'I know everything about std::string'): http://goo.gl/huJN6X
icc ( ancient: Oct 2012 vintage) : http://goo.gl/ivLKmW (Three years is an eon for C++ optimisers)


Inheriting constructors: http://www.stroustrup.com/C++11FAQ.html#inheriting
In-class member initializers: http://www.stroustrup.com/C++11FAQ.html#member-init
Topic archived. No new replies allowed.