std::sort / sorting using member function

I have trouble with std::sort using a member function as the third parameter.

below is a part of my code that I need help with... I am a beginner and quite new to this website and have read the instruction..so I am trying to make my question as short and precise as possible. If you need more code to answer my question please let me know.

Using a non-member function, this works perfectly fine fine.
1
2
3
4
5
6
7
8
9
10
11
12
13
14

   // main
  std::sort(figure.begin(), figure.end(), shapekin::lessThan);
  display(figure);
   .
   .
   .
// in my class file
// shape is my abstract base class
  bool lessThan(const shape*, const shape*);
  bool lessThan(const shape* lVal, const shape* rVal)
	{
		return lVal->area() < rVal->area();
	}


but If I try sorting it the same way with a member function, though it does not throw an error, it does not sort at all.
1
2
3
4
5
6
7
8
9
10
11
12
// main
std::sort(figure.begin(), figure.end(), shapekin::shape::lessThan);
 .
 . 
 .
// in my class file
// shape is my abstract base class
bool lessThan(const shape*, const shape*);
bool shape::lessThan(const shape* lVal, const shape* rVal)
   {
     return lVal->area() < rVal->area();
   }



Also I've tried this way too and it doesn't work either.
1
2
3
4
5
6
7
8
9
10
11
12
// main
std::sort(figure.begin(), figure.end());
.
.
.
// in my class file
// shape is my abstract base class
bool operator<(shape*)const;
bool shape::operator<(shape* rVal)const
	{
		return this->area() < rVal->area();
	}


I am aware that my question is confusing and doesn't provide enough.. My code is 8 separate files so I didn't know how to ask and didn't want to post all the code and make this question long... please let me know

Thanks!
Last edited on
comp -- Binary function that accepts two elements in the range as arguments, and returns a value convertible to bool.

In other words, the sort will call
bool result = comp( lhs, rhs );

Your member function must be called:
bool result = obj.lessThan( lhs, rhs );
The sort does not have 'obj'.

The operator< is called:
bool result = lhs < rhs;
The lhs and rhs are both elements of figure. They are either both shapes or both pointers.
In your operator the lhs must be a shape, but the rhs must be a pointer. Different types.


A static member function does not have a 'this' object.


The lessThan() does not have to be a member. It is still a part of the logical interface of shapes.
If polymorphism is a must, what about a solution like the following (adjusted from stackoverflow.com):
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <algorithm>
#include <iostream>
#include <limits>
#include <set>
#include <vector>

class Shape {
public:
    Shape(double area_arg) : area {area_arg} {}
    bool operator<(const Shape& other) const;
    struct MyComp {
        bool operator()(const Shape* left, const Shape* right) const;
    };
protected:
    double area {};
    virtual void donothing() = 0;
};

bool Shape::operator<(const Shape& other) const
{ return area < other.area; }

bool Shape::MyComp::operator()(const Shape* left, const Shape* right) const
{ return left->area < right->area; }

class Nonagon : public Shape {
public:
    Nonagon(double value) : Shape(value) {}
protected:
    void donothing() override { /* still doing nothing */ }
    friend std::ostream& operator<<(std::ostream& os, const Nonagon& other)
    { return (os << other.area << ' '); }
};

void waitForEnter();

int main()
{
    std::vector<Nonagon*> vnine1 { new Nonagon{3.3}, new Nonagon{1.1}, 
                                   new Nonagon{4.4}, new Nonagon{2.2} };
    std::cout << "vnine1 before: ";
    for(const auto e : vnine1) { std::cout << *e; }
    std::cout << '\n';
    std::sort(vnine1.begin(), vnine1.end(), Shape::MyComp());
    std::cout << "vnine1 after: ";
    for(const auto e : vnine1) { std::cout << *e; }
    std::cout << '\n';

    std::vector<Nonagon> vnine2 { 3.3, 1.1, 4.4, 2.2 };
    std::cout << "vnine2 before: ";
    for(const auto e : vnine2) { std::cout << e; }
    std::cout << '\n';
    std::sort(vnine2.begin(), vnine2.end());
    std::cout << "vnine2 after: ";
    for(const auto e : vnine2) { std::cout << e; }
    std::cout << '\n';

    std::set<Nonagon*, decltype(Shape::MyComp())> snine1 { new Nonagon{3.3}, 
                                                           new Nonagon{1.1}, 
                                                           new Nonagon{4.4}, 
                                                           new Nonagon{2.2} };
    std::cout << "snine1: ";
    for(const auto e : snine1) { std::cout << *e; }
    std::cout << '\n';

    std::set<Nonagon> snine2 { 3.3, 1.1, 4.4, 2.2 };
    std::cout << "snine2: ";
    for(const auto e : snine2) { std::cout << e; }
    std::cout << '\n';

    waitForEnter();
    return 0;
}

void waitForEnter()
{
    std::cout << "\nPress ENTER to continue...\n";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

vnine1 before: 3.3 1.1 4.4 2.2
vnine1 after: 1.1 2.2 3.3 4.4
vnine2 before: 3.3 1.1 4.4 2.2
vnine2 after: 1.1 2.2 3.3 4.4
snine1: 1.1 2.2 3.3 4.4
snine2: 1.1 2.2 3.3 4.4

Press ENTER to continue...

Apart from memory leaks (I wouldn’t use a container of raw pointers in a ‘real life’ code) and virtual desctructors..., is it so bad?
Thanks guys! I sincerely appreciate it. looking into you guys answers/solutions I've learned a lot that I didn't know! however... if you don't mind could you guys take a look at my code? I feel kind of challenged and would like to get mine to work as opposed to copying your solutions... ( I haven't learned "auto", "struct" and a few other things .. I will have to work on them)

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#include <iostream>
#include <ctime>
#include <algorithm>
#include <vector>
#include <cassert>
#include <string>
#include <cmath>
#include <iomanip>
static const unsigned short SHAPE = 106;
static const std::string SHAPES[SHAPE] = { "balbis", "concave polygon", "constructible polygon", "convex polygon", "cyclic polygon", "equiangular polygon", "equilateral polygon", "Penrose tile", "polyform", "regular polygon", "simple polygon", "tangential polygon", "henagon", "digon", "triangle", "acute triangle", "equilateral triangle", "isosceles triangle", "obtuse triangle", "rational triangle", "right triangle", "30-60-90 triangle", "isosceles right triangle", "Kepler triangle", "scalene triangle", "quadrilateral", "cyclic quadrilateral", "square", "diamond", "kite", "parallelogram", "rhombus", "Lozenge", "rhomboid", "rhomb", "rectangle", "square", "rectagon", "quadrangle", "quadragon", "tangential quadrilateral", "trapezus", "trapezoid", "isosceles trapezoid", "pentagon", "regular pentagon", "pentagonoid", "hexagon", "Lemoine hexagon", "heptagon", "octagon", "regular octagon", "nonagon", "decagon", "regular decagon", "hendecagon", "dodecagon", "hexadecagon", "icosagon", "swastika", "star without crossing lines", "star polygon", "hexagram", "star of David", "heptagram", "octagram", "star of Lakshmi", "decagram", "pentagram", "annulus", "arbelos", "circle", "Archimedes' twin circles", "Bankoff circle", "circumcircle", "disc", "excircle", "incircle", "nine-point circle", "circular sector", "circular segment", "crescent", "lens, vesica piscis", "lune", "Reuleaux polygon", "Reuleaux triangle", "salinon", "semicircle", "tomahawk", "triquetra", "Archimedean spiral", "astroid", "cardioid", "deltoid", "ellipse", "heart", "heartagon", "various lemniscates", "oval", "Cartesian oval", "Cassini oval", "Oval of Booth", "ovoid", "superellipse", "taijitu", "tomoe, magatama" };
static const double PI = 3.14;

// Abstract base class
class shape
{
public:
	shape(const std::string& sIn = "balbis");
	virtual~shape();
	void set(const std::string&);
	std::string name()const;
	virtual double area()const = 0;
protected:
	unsigned short index;
};
shape::shape(const std::string& sIn) : index(0)
{
	set(sIn);
}
shape::~shape()
{
}
void shape::set(const std::string& sIn)
{
	index = 0;
	for (unsigned short i = 0; i < SHAPE; ++i)
	{
		if ((index < SHAPE) && (SHAPES[i] == sIn))
			index = i;
	}
	assert(index < SHAPE);
}
std::string shape::name()const
{
	return SHAPES[index];
}
double shape::area()const
{
	return 0.0;
}
// sub class inherting shape
class circle : public shape
{
public:
	circle(const double rIn = 1.0);
	virtual~circle();
	void set(const double rIn);
	virtual double area()const;

protected:
	double radius;
};
circle::circle(const double rIn) : shape("circle")
{
	assert(rIn > 0.0);
	set(rIn);
}
circle::~circle()
{
}
void circle::set(const double rIn)
{
	radius = rIn;
}
double circle::area()const
{
	return PI * pow(radius, 2);
}
// sub class inherting shape
class square : public shape
{
public:
	square(const double sIn = 1.0);
	virtual~square();
	void set(const double);
	virtual double area()const;
protected:
	double side;
};
square::square(const double sIn) : shape("square")
{
	assert(sIn > 0.0);
	set(sIn);
}
square::~square()
{
}
void square::set(const double sIn)
{
	side = sIn;
}
double square::area()const
{
	return pow(side, 2);
}
// sub class inherting shape
class triangle : public shape
{
public:
	triangle(const double s1 = 1.0, const double s2 = 1.0, const double s3 = 1.0);
	triangle(const double*);
	virtual~triangle();
	void set(const double, const double, const double);
	void set(const double*);
	double getPerimeter()const;
	virtual double area()const;
protected:
	double* sideLength;
};
triangle::triangle(const double s1, const double s2, const double s3) : shape("triangle")
{
	assert((s1 > 0.0) && (s2 > 0.0) && (s3 > 0.0));
	sideLength = new double[3];
	set(s1, s2, s3);
}
triangle::triangle(const double* sIn)
{
	assert((sIn[0] > 0.0) && (sIn[1] > 0.0) && (sIn[2] > 0.0));
	sideLength = new double[3];
	set(sIn);
}
triangle::~triangle()
{
}
void triangle::set(const double s1, const double s2, const double s3)
{
	sideLength[0] = s1;
	sideLength[1] = s1;
	sideLength[2] = s1;
}
void triangle::set(const double* sIn)
{
	sideLength[0] = sIn[0];
	sideLength[1] = sIn[1];
	sideLength[2] = sIn[2];
}
double triangle::getPerimeter()const
{
	return sideLength[0] + sideLength[1] + sideLength[2];
}
double triangle::area()const
{
	double semiP = getPerimeter() * 0.5;
	return sqrt(semiP*(semiP - sideLength[0])*(semiP - sideLength[1])*(semiP - sideLength[2]));
}
// Main
bool triValTest(const double, const double, const double);
bool lessThan(shape*, shape*);
void display(std::vector<shape*>);
int main(void)
{
	std::vector<shape*> figure(10);
	srand((unsigned)time(NULL));
	double triArr[3];

	for (unsigned short i = 0; i < figure.size(); ++i)
	{
		switch (rand() % 3)
		{
		case 0: figure[i] = new circle((rand() % 100 + 1) / 10.0);
			break;
		case 1: figure[i] = new square((rand() % 100 + 1) / 10.0);
			break;
		case 2:
			do
			{
				triArr[0] = ((rand() % 100 + 1) / 10.0);
				triArr[1] = ((rand() % 100 + 1) / 10.0);
				triArr[2] = ((rand() % 100 + 1) / 10.0);
			} while (!(triValTest(triArr[0], triArr[1], triArr[2])));
			figure[i] = new triangle((rand() % 100 + 1) / 10.0);
			break;
		}
	}

	display(figure);

	std::sort(figure.begin(), figure.end(), lessThan);
	std::cout << std::endl;
	display(figure);
	return 0;
}
bool triValTest(const double s1, const double s2, const double s3)
{
	return (s1 + s2 > s3) && (s1 + s3 > s2) && (s2 + s3 > s1);
}
void display(std::vector<shape*> figure)
{
	std::cout << std::fixed;
	std::cout << std::setprecision(6);
	std::cout << "Name \t\t Area" << std::endl;
	for (unsigned short c = 0; c < figure.size(); ++c)
	{
		std::cout << c + 1 << ":" << figure[c]->name() << "\t" << figure[c]->area() << std::endl;
	}
}
bool lessThan(shape* lVal, shape* rVal)
{
	return lVal->area() < rVal->area();
}


above works fine but I'd like to know, out of curiosity, how to use the void "lessThan" function as a member function..using either

1
2
3
4
bool shape::lessThan(shape* lVal, shape* rVal)
{
	return lVal->area() < rVal->area();
} 


or like this

1
2
3
4
bool shape::operator<(shape* rVal)
{
return area()<rVal->area();
}


What should I do to make it work...? Thank you very much...
how to use the void "lessThan" function as a member function

Many changes are just a matter of personal taste and I haven’t tested it extensively:
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#include <algorithm>
#include <cassert>
#include <cmath>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <valarray>
#include <vector>

constexpr double PI = std::acos(-1);

// Abstract base class
class shape
{
public:
    static std::vector<std::string> shape_names;

    shape(const std::string& sIn = "balbis");
    virtual ~shape() = default;
    void set(const std::string&);
    std::string getName() const;
    // If in the future you want to store a value for your shape area, a 
    // const function won't be able to modify it.
    virtual double area() const = 0;
    struct lessThen {
        bool operator()(const shape* left, const shape* right) const
        {
            return left->area() < right->area();
        }
    };
protected:
    static bool shape_names_loaded;
    unsigned short index           {};

    void load_names(std::string filename = "shapes.txt");
};

bool shape::shape_names_loaded { false };
std::vector<std::string> shape::shape_names {};

shape::shape(const std::string& sIn)
{
    if(!shape_names_loaded) {
        load_names();
        shape_names_loaded = true;
    }
    set(sIn);
}

void shape::set(const std::string& sIn)
{
    index = 0;
    for (unsigned short i = 0; i < shape_names.size(); ++i)
    {
        if (shape_names.at(i) == sIn) { index = i; }
    }
    // assert() does nothing if NDEBUG is defined
    // assert(index < SHAPE);
    assert(index < shape_names.size());
}

void shape::load_names(std::string filename)
{
    std::ifstream in(filename);
    std::string sname;
    while(std::getline(in, sname)) { shape_names.push_back(sname); }
    in.close(); // in case of future modifications
}

std::string shape::getName()const
{
    return shape_names[index];
}

// sub class inherting shape
class circle : public shape
{
public:
    circle(const double rIn = 1.0);
    virtual~circle() = default;
    void set(const double rIn);
    virtual double area() const override;

protected:
    double radius;
};

circle::circle(const double rIn) : shape("circle")
{
    assert(rIn > 0.0);
    set(rIn);
}

void circle::set(const double rIn)
{
    radius = rIn;
}

double circle::area() const
{
    return PI * pow(radius, 2);
}

// sub class inherting shape
class square : public shape
{
public:
    square(const double sIn = 1.0);
    virtual~square() = default;
    void set(const double);
    virtual double area() const override;
protected:
    double side;
};

square::square(const double sIn) : shape("square")
{
    assert(sIn > 0.0);
    set(sIn);
}

void square::set(const double sIn)
{
    side = sIn;
}

double square::area() const
{
    return pow(side, 2);
}

// sub class inherting shape
class triangle : public shape
{
public:
    triangle(double s1 = 1.0, double s2 = 1.0, double s3 = 1.0);
    triangle(std::valarray<double>);
    triangle(const double*);
    virtual ~triangle() = default;
    void set(const double, const double, const double);
    void set(const std::valarray<double>&);
    void set(const double*);
    double getPerimeter() const;
    double area() const override;
protected:
    std::valarray<double> side_length;
};

triangle::triangle(const double s1, const double s2, const double s3) : shape("triangle")
{
    assert((s1 > 0.0) && (s2 > 0.0) && (s3 > 0.0));
    set(s1, s2, s3);
}

triangle::triangle(std::valarray<double> sIn) : shape("triangle")
{
    assert(!(sIn > 0.0).min());
    set(sIn);
}

triangle::triangle(const double* sIn) : shape("triangle")
{
    assert((sIn[0] > 0.0) && (sIn[1] > 0.0) && (sIn[2] > 0.0));
    set(sIn);
}

void triangle::set(const double s1, const double s2, const double s3)
{
    side_length = { s1, s2, s3 };
}

void triangle::set(const std::valarray<double>& sIn)
{
    side_length = sIn;
}

void triangle::set(const double* sIn)
{
    side_length.resize(3);
    for(auto& e : side_length) { e = *sIn++; }
}

double triangle::getPerimeter() const
{
    return side_length.sum();
}

double triangle::area()const
{
    double semiP = getPerimeter() * 0.5;
    return sqrt( semiP * (semiP - side_length[0])
                       * (semiP - side_length[1])
                       * (semiP - side_length[2]) );
}

bool triValTest(const double, const double, const double);
void display(std::vector<shape*>);

// Main
int main(void)
{
    std::vector<shape*> figure(10);
    srand((unsigned)time(NULL));

    for (unsigned short i = 0; i < figure.size(); ++i)
    {
        double triArr[3];
        switch (rand() % 3)
        {
        case 0: figure[i] = new circle((rand() % 100 + 1) / 10.0);
            break;
        case 1: figure[i] = new square((rand() % 100 + 1) / 10.0);
            break;
        case 2:
            do
            {
                triArr[0] = ((rand() % 100 + 1) / 10.0);
                triArr[1] = ((rand() % 100 + 1) / 10.0);
                triArr[2] = ((rand() % 100 + 1) / 10.0);
            } while (!(triValTest(triArr[0], triArr[1], triArr[2])));
            figure[i] = new triangle(triArr);
            break;
        }
    }

    display(figure);

    std::sort(figure.begin(), figure.end(), shape::lessThen());
    std::cout << '\n';
    display(figure);
    return 0;
}

bool triValTest(const double s1, const double s2, const double s3)
{
    return (s1 + s2 > s3) && (s1 + s3 > s2) && (s2 + s3 > s1);
}

void display(std::vector<shape*> figure)
{
    std::cout << std::fixed << std::setprecision(6) << "Name \t\t Area\n";
    for (size_t c = 0; c < figure.size(); ++c)
    {
        std::cout << c + 1 << ":" << figure.at(c)->getName() << "\t" 
                  << figure.at(c)->area() << '\n';
    }
}


shapes.txt:

balbis
concave polygon
constructible polygon
convex polygon
cyclic polygon
equiangular polygon
equilateral polygon
Penrose tile
polyform
regular polygon
simple polygon
tangential polygon
henagon
digon
triangle
acute triangle
equilateral triangle
isosceles triangle
obtuse triangle
rational triangle
right triangle
30-60-90 triangle
isosceles right triangle
Kepler triangle
scalene triangle
quadrilateral
cyclic quadrilateral
square
diamond
kite
parallelogram
rhombus
Lozenge
rhomboid
rhomb
rectangle
square
rectagon
quadrangle
quadragon
tangential quadrilateral
trapezus
trapezoid
isosceles trapezoid
pentagon
regular pentagon
pentagonoid
hexagon
Lemoine hexagon
heptagon
octagon
regular octagon
nonagon
decagon
regular decagon
hendecagon
dodecagon
hexadecagon
icosagon
swastika
star without crossing lines
star polygon
hexagram
star of David
heptagram
octagram
star of Lakshmi
decagram
pentagram
annulus
arbelos
circle
Archimedes' twin circles
Bankoff circle
circumcircle
disc
excircle
incircle
nine-point circle
circular sector
circular segment
crescent
lens
vesica piscis
lune
Reuleaux polygon
Reuleaux triangle
salinon
semicircle
tomahawk
triquetra
Archimedean spiral
astroid
cardioid
deltoid
ellipse
heart
heartagon
various lemniscates
oval
Cartesian oval
Cassini oval
Oval of Booth
ovoid
superellipse
taijitu
tomoe
magatama

Thank You VERY VERY MUCH~~~~~
Topic archived. No new replies allowed.