STL and std custom compare arguments working in c++ - c++

I asked this question on stackoverflow STL passing object
I got to know that we pass objects which in tern call the compare operator in them and compare our values and gives us a result. All good.
Now in this piece of code:
class Compare {
public:
bool operator ()(const int &a, const int &b) { return a < b;}
};
void solve(){
set<int, Compare> s;
vector<int> arr = {1,4,6,3,7,2,8,588,5};
for(int e : arr) s.insert(e);
for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl;
}
I get error if I do Compare() which is what I thought must be done(passing an object). Why so? How can we pass Compare?
Also in this piece of code I cannot pass a function like:
bool comp(const int &a, const int &b) { return a < b; }
void solve(){
set<int, comp> s;
vector<int> arr = {1,4,6,3,7,2,8,588,5};
for(int e : arr) s.insert(e);
for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl;
}
Which is what I expect. Not passing functions.
Now in this piece of code:
bool comp(const int &a, const int &b) { return a < b; }
void solve(){
vector<int> arr = {1,4,6,3,7,2,8,588,5};
nth_element(arr.begin(), arr.begin()+3, arr.end(), comp);
for(int e : arr) cout << e << " "; cout << endl;
}
I am able to pass a function and there is no error. I mean what is going on? From my perspective, we should just pass objects and they will call their () overloaded operator and compare values but sometimes we are passing functions, sometimes passing objects giving error and sometimes we just pass class name. What is exactly going on in c++

Second template parameter of std::set is a type.
You might use function pointer:
bool comp(const int &a, const int &b) { return a < b; }
std::set<int, bool (*)(const int &, const int &)> s(comp);

In the first code snippet the compiler issues an error because you was using an object Compare() as the type template argument instead of the type Compare.
If you will write for example
#include <iostream>
#include <vector>
#include <set>
using namespace std;
class Compare {
public:
bool operator ()(const int &a, const int &b) const { return a < b;}
};
int main()
{
set<int, Compare> s( ( Compare() ) );
vector<int> arr = {1,4,6,3,7,2,8,588,5};
for(int e : arr) s.insert(e);
for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl;
return 0;
}
then the program compiles successfully and its output is
588 8 7 6 5 4 3 2 1
That is as the second type template argument of the class template std::set you need to use the type specifier Compare. But if you want to specify the argument of the constructor you need to use an object of the type of the template argument.
The same problem exists for the second code snippet. You need to write like it is shown in this demonstrative program.
#include <iostream>
#include <vector>
#include <set>
using namespace std;
bool comp(const int &a, const int &b) { return a < b; }
int main()
{
set<int, bool ( * )( const int &, const int & )> s( comp );
vector<int> arr = {1,4,6,3,7,2,8,588,5};
for(int e : arr) s.insert(e);
for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl; return 0;
}
The program output is the same as shown above.
Pay attention to that the class template std::set is declared like
template <class Key, class Compare = less<Key>,
class Allocator = allocator<Key> >
class set;
So if you specified explicitly the second template argument (instead of using by default the function object std::less<Key>) then you need to provide a corresponding object of this type in a constructor of the class template std::set.

operator() must be const. That's the API.
class Compare {
public:
bool operator ()(const int &a, const int &b) const { return a < b; }
}

Related

how to create an overload operator for adding a new element to the beginning or end of a vector с++

First I would like to apologize for the quality of my code, I'm just learning.
I have a university assignment.
String concatenation and adding one character to a string (like
on the left and on the right). Implement using overloading
the operator.
The question is this:
I need to implement two overloads (operator+)
First: adding one element to the end of the vector ( + 'e', ​​for example ).
Second: adding an element to the beginning of the vector ('e' + , for example).
I have problems in order to implement the second part of the assignment.
I searched similar questions on stackoverflow, but they did not help me much.
Here is my code:
#include <iostream>
#include <vector>
using namespace std;
template <class T>
class String
{
private:
public:
vector<T> ptr_string;
String() // default value constructor (empty string)
{
ptr_string.push_back('\0');
}
String(const String& other) // copy constructor of the same type
{
int n = other.getLength();
for (int i = 0; i < n; i++)
{
ptr_string.push_back(other.ptr_string[i]);
}
}
String(T symbol, int n) // n times repeated value constructor
{
for (int i = 0; i < n; i++)
{
ptr_string.push_back(symbol);
}
}
String(String&& a) // move constructor
: ptr_string(a.ptr_string)
{
a.ptr_string = nullptr;
}
int getLength() const
{
return ptr_string.size();
}
void printString() const
{
int i = 0;
for (int i = 0; i < ptr_string.size(); i++)
{
cout << ptr_string[i];
}
cout << endl;
}
template <typename T2>
auto operator+(T2 b)
{
ptr_string.push_back(b);
return ptr_string;
}
auto operator+(String const& a)
{
ptr_string.push_back(a);
return ptr_string;
}
};
int main()
{
String<char> P = String<char>('P', 10);
P + 'e';
P.printString();
'e' + P;
P.printString();
}
I tried to pass a reference to a vector as a parameter, but I ran into a problem that this is most likely not the right solution.
auto operator+( String const& a)
{
ptr_string.push_back(a);
return ptr_string;
}
String<char> P = String<char>( 'P', 10);
'e' + P;
P.printString();
expected result: ePPPPPPPPPP
First, operator+ should not modify the current object. It should not return a vector but a new String<T>. It should be const.
Your char,int constuctor misses to add a nullterminator. Maybe that was on purpose, but I changed it because I am using that constructor. Moreover, I removed the move constructor, because its not needed yet. In its implementation you assign nullptr to the vector which is wrong. You need not implement the move constructor, you can declare it as =default;. This is what I also did for the copy constructor, because the compiler generated copy constructor is as good (or better) than your self written one.
Then, there is no + for 'e'+String in your code. When implmented as member then this is always the left operand. You can implement it as free function.
#include <iostream>
#include <vector>
template<class T>
class String {
public:
std::vector<T> ptr_string;
String() { ptr_string.push_back('\0'); }
String(const String& other) = default;
String(T symbol, int n) {
for (int i = 0; i < n; i++) {
ptr_string.push_back(symbol);
}
ptr_string.push_back('\0');
}
int getLength() const {
return ptr_string.size();
}
void printString() const {
int i = 0;
for (int i = 0; i < ptr_string.size(); i++) {
std::cout << ptr_string[i];
}
std::cout << std::endl;
}
template<typename T2>
auto operator+( T2 b) const {
String res = *this;
res.ptr_string.push_back(b);
return res;
}
auto operator+( String const& a) {
String res = *this;
for (const auto& c : a.ptr_string) res.ptr_string.push_back(c);
return res;
}
};
template<typename T2,typename T>
auto operator+(const T2& b,const String<T>& a) {
return String<T>(b,1) + a;
}
int main() {
String<char> P = String<char>( 'P', 10);
auto Y = P + 'e';
P.printString();
Y.printString();
auto Z = 'e' + P;
P.printString();
Z.printString();
}
Demo
This is just minimum changes on your code. I would actually implement also the other operators outside of the class as free functions. The loop in the char,int constructor should be replaced with the appropriate vector constructor and perhaps there is more which can be improved. For more on operator overloading I refer you to https://en.cppreference.com/w/cpp/language/operators and What are the basic rules and idioms for operator overloading?
I don't think your assignment really cover a scenario that happens in real life. It is full of code smell. As #463035818_is_not_a_number mentionned operator+ usually don't modify the current object. But it is technically possible, See below. Vector are not meant to "push_front", avoid 'push_front' on vector as a general rule (here, i use deque for ex).
#include <deque>
#include <iostream>
#include <string>
template<class T>
struct ConcatenableDeq
{
std::deque<T> _deq;
ConcatenableDeq(std::deque<T> deq) : _deq(deq) {};
void print() const
{
for (const auto& e : this->_deq)
std::cout << e << " ";
std::cout << std::endl;
}
friend void operator+(ConcatenableDeq<T>& cd, const T& v)
{
cd._deq.push_back(v);
}
friend void operator+(const T& v, ConcatenableDeq<T>& cd)
{
cd._deq.push_front(v);
}
};
int main(int argc, char* argv[])
{
ConcatenableDeq<char> cd({ '1', '2', '3' });
cd.print();
cd + '4';
cd.print();
'0' + cd;
cd.print();
// output:
// 1 2 3
// 1 2 3 4
// 0 1 2 3 4
}

Functional Object in Polymorphism

I want to implement Functional Object in Polymorphism as follows:
#include <algorithm>
#include <iostream>
using namespace std;
struct Compare {
virtual bool operator() (int, int) const = 0;
};
struct Less : public Compare {
bool operator() (int i, int j)
const {
return (i < j);
}
};
struct Greater : public Compare {
bool operator() (int i, int j)
const {
return (i > j);
}
};
void f(const Compare& c) {
int arr[10] = { 4,2,6,7,1,3,5,9,8,0 };
sort(arr, arr + 10, c);
for (int i = 0; i < 10; ++i)
cout << arr[i] << " ";
}
int main()
{
f(Less());
f(Greater());
}
But it has an error message "no instance of overloaded function "sort" matches the argument list"
I think that the abstract class cannot have an instance.
How can I fix it?
std::sort wants to copy the ordering function.
There is a standard class that "wraps" a reference and makes it copyable; std::ref.
#include <memory>
... and then ...
sort(arr, arr + 10, std::ref(c));
std::sort takes the comparator parameter by-value; you can't pass an abstract class like Compare to it. You can pass Less or Greater directly to it.
You can make f template,
template <typename C>
void f(const C& c) {
int arr[10] = { 4,2,6,7,1,3,5,9,8,0 };
sort(arr, arr + 10, c);
for (int i = 0; i < 10; ++i)
cout << arr[i] << " ";
}
then pass Less or Greater to it like:
f(Less());
f(Greater());
LIVE

C++ Sorting vector of structs with const variables alphabetically

Hello I wanted to know if it was possible to do a thing like this ? // THANKS ! :)
struct PET
{
const char* pet;
const int age;
};
bool Sort(const PET& first, const PET& second)
{
return first.pet < second.pet;
}
void Foo(const std::vector<PET> pets)
{
std::sort(pets.begin(), pets.end(), Sort); /* Does not work */
std::cout << pets[0].pet;
std::cout << pets[0].age;
}
I completely agree with # Ulrich Eckhardt.
You cannot sort the vector as because the elements of your vector are not assignable.
I think, you might have gone confused with usage of const.
There is no need to make the structure variables const. The parameter of the custom sort function are generally kept as const because they should not be modifiable. This is a pattern which ensures safe coding practice.
Also, if you are using C++, I would suggest to use std::string instead of char*, as std::string is a cleaner, safer way to go because it removes the burden of memory management from the programmer.
Have a look at the working implementation, without use of const:
#include <string.h>
#include<iostream>
#include<vector>
#include<algorithm>
struct PET
{
std::string name;
int age;
};
bool compare(const struct PET& a, const struct PET& b){
return (a.name.compare(b.name) <= 0) ? true : false;
}
int main(){
std::vector<struct PET> vec(3);
vec[0].name = "dog";
vec[0].age = 3;
vec[1].name = "cat";
vec[1].age = 1;
vec[2].name = "bird";
vec[2].age = 2;
sort(vec.begin(), vec.end(), compare);
for(int i=0;i<3;i++){
std::cout<<vec[i].name<<" "<<vec[i].age<<std::endl;
}
return 0;
}
As #Deepak Tatyaji Ahire and #Ulrich Eckhardt said, you can't do what you wrote in your code.
const int can't be a variable. It is a constant for definition :)
The vector you wrote in your code can't be built that way.
I did not understand what you wanted to do with the "sort" function, I wrote the following to code, maybe it could help:
#include<iostream>
#include<vector>
struct PET
{
const char* pet;
int age;
PET(const char* c, int a) : pet(c) , age(a) {}
};
void Foo(PET &p, std::vector<PET> &v)
{
v.push_back(p);
/*do something here if needed*/
}
int main()
{
std::vector<PET> vect;
PET cat("Cat", 5);
PET dog("Dog", 10);
PET bird("Bird", 2);
Foo(cat, vect);
Foo(dog, vect);
Foo(bird, vect);
/*this is not elegant, you could define a function that give a list of
({Animal, age},...) to vector and then pushes back all these elements to the vector*/
for(int i=0; i<3; i++) std::cout<< vect[i].pet << ' ' << vect[i].age << std::endl; //std::cout << vect; if you are using an operator << overload
/*to overload the << operator in order to able to print the vector of struct PET:
std::ostream & operator << (std::ostream &os, std::vector<PET> &p)
{
os << "<";
for (int i = 0; i < p.size(); i++) {
os << p[i].pet;
os << ", ";
os << p[i].age;
if (i != p.size() - 1)
os << " - ";
}
os << ">\n";
return os;
}
*/
return 1;
}
AFAIK, there's no way to directly compare structures without defining their comparator.
Though in C++20, it introduces three-way comparison and you might be allowed to declare the Default comparisons by a single line. Very convenient. Unfortunately, there haven been no compiler implementing this feature.
For now, you have to manually define the comparator
inline bool cmp(const PET &lhs, const PET &rhs)
{
return std::strcmp(lhs.pet, rhs.pet)<0;
}
and pass it to std::sort

How to compare two objects using templates?

I want to compare two objects and get larger among them using templates. Passing the object as an argument isn't working as the code below. See the sample code given below. That's what I'm trying to do.
#include <iostream>
using namespace std;
template <class T>
class max
{
T a;
public:
max(T a)
{
this.a = a;
}
T Large(T n2)
{
return (a > n2.a) ? a : n2.a;
}
};
int main()
{
max <int> obj1(10);
max <int> obj2(20);
cout<<obj1.Large(obj2)<<" is larger"<<endl;
return 0;
}
I'm doing something like this but by comparing 2 objects.
// class templates
#include <iostream>
using namespace std;
template <class T>
class mypair {
T a, b;
public:
mypair (T first, T second)
{a=first; b=second;}
T getmax ();
};
template <class T>
T mypair<T>::getmax ()
{
T retval;
retval = a>b? a : b;
return retval;
}
int main () {
mypair <int> myobject (100, 75);
cout << myobject.getmax();
return 0;
}
This code seems to work fine, let me know if this helps
#include <iostream>
using namespace std;
template <class T>
class Maxi {
T a;
public:
Maxi(T val)
{
this->a = val;
}
T Large(maxi n2)
{
return (a > n2.a) ? a : n2.a;
}
};
int main() {
Maxi <int> obj_1(10);
Maxi <int> obj_2(20);
cout<<obj_1.Large(obj_2)<<" is larger"<<endl;
return 0;
}
There is already a max and min template function provided under algorithm.
Just call std::max(10, 20) to get the larger between the two.
You can even provide your own comparator for custom comparison.
Side note: It seems like by including iostream, you can use max and min without algorithm.
Problems I see:
Use of
using namespace std;
is probably going to mess things up for you since std::max gets pulled into the mix indirectly. It does for me with g++. Don't use it.
Syntax error in the following line:
this.a = a;
That needs to be
this->a = a;
Argument to Large needs to be of type max, not T.
For good measure, I would also make it a const member function.
T Large(max n2) const
{
return (a > n2.a) ? a : n2.a;
}
Use std::cout and std::endl since using namespace std; is problematic.
Here's an updated version of your code with the above fixes:
#include <iostream>
template <class T>
class max
{
private:
T a;
public:
max(T a)
{
this->a = a;
}
T Large(max n2) const
{
return (a > n2.a) ? a : n2.a;
}
};
int main()
{
max <int> obj1(10);
max <int> obj2(20);
std::cout << obj1.Large(obj2) << " is larger"<<std::endl;
return 0;
}
It works for me and produces the following output:
20 is larger
I think you're trying to do this:
template <typename T>
T max(const T &a, const T &b)
{ return (b < a) ? a : b; }
That's how you do that. Your weird mishmash of a class doesn't make any sense at all. And so if I got what you're trying to do wrong, please explain yourself better.
Templates don't have to be classes you know. You can have templated functions.
If you absolutely must use a class, do this then:
template <typename T>
class max
{
T operator ()(const T &a, const T &b) { return (b < a) ? a : b; }
};
int main()
{
max<int> foo;
cout << foo(10, 20) << " is larger\n"; // Don't use endl most of the time.
return 0;
}
Suppose you have two objects that aren't ints, what do you do? Well, you do this:
#include <iostream>
#include <algorithm>
struct A {
int v1;
int v2;
};
bool operator <(A &a, A &b)
{
return (a.v1 < b.v1) || ((a.v1 == b.v1) && (a.v2 < b.v2));
}
::std::ostream &operator <<(::std::ostream &os, const A &a)
{
os << "{" << a.v1 << ", " << a.v2 << "}";
}
int main()
{
A first{10, 20};
B second{20, 10};
::std::cout << ::std::max(first, second) << " is the larger.\n";
}
If you don't want to have to define operator <, then do this:
bool my_less_than(const A &a, const A &b)
{
return (a.v1 < b.v1) || ((a.v1 == b.v1) && (a.v2 < b.v2));
}
int main()
{
A first{10, 20};
B second{20, 10};
::std::cout << ::std::max(first, second, my_less_than) << " is the larger.\n";
}

Why do I get an error in "forming reference to reference type" map?

What is the alternative if I need to use a reference, and the data I am passing I cannot change the type of, hence I cannot really store a pointer to it?
Code:
#include <map>
#include<iostream>
#include<string>
using namespace std;
int main()
{
string test;
pair<string, string> p=pair<string, string>("Foo","Bar");
map<pair<string, string>, string&> m;
m[make_pair("aa","bb")]=test;
return 0;
}
Error:
$ g++ MapPair.cpp
/usr/include/c++/3.2.3/bits/stl_map.h:
In instantiation of
std::map<std::pair<std::string,
std::string>, std::string&,
std::less<std::pair<std::string,
std::string> >,
std::allocator<std::pair<const
std::pair<std::string, std::string>,
std::string&> > >': MapPair.cpp:15:
instantiated from here
/usr/include/c++/3.2.3/bits/stl_map.h:221:
forming reference to reference type
std::string&' MapPair.cpp: In
function int main()': MapPair.cpp:16:
no match for
std::map, std::string&,
std::less >,
std::allocator,
std::string&> > >& [std::pair]' operator
/usr/include/c++/3.2.3/bits/stl_pair.h:
At global scope:
/usr/include/c++/3.2.3/bits/stl_pair.h:
In instantiation of std::pair<const
std::pair<std::string, std::string>,
std::string&>':
/usr/include/c++/3.2.3/bits/stl_tree.h:122:
instantiated from
std::_Rb_tree_node
What am I doing wrong to cause this errror?
You cannot store references. References are just aliases to another variable.
The map needs a copy of the string to store:
map<pair<string, string>, string> m;
The reason you are getting that particular error is because somewhere in map, it's going to do an operation on the mapped_type which in your case is string&. One of those operations (like in operator[], for example) will return a reference to the mapped_type:
mapped_type& operator[](const key_type&)
Which, with your mapped_type, would be:
string&& operator[](const key_type& _Keyval)
And you cannot have a reference to a reference:
Standard 8.3.4:
There shall be no references to references, no arrays of references, and no pointers to references.
On a side note, I would recommend you use typedef's so your code is easier to read:
int main()
{
typedef pair<string, string> StringPair;
typedef map<StringPair, string> StringPairMap;
string test;
StringPair p("Foo","Bar");
StringPairMap m;
m[make_pair("aa","bb")] = test;
return 0;
}
The previous answers here are outdated. Today we have std::reference_wrapper as part of the C++11 standard:
#include <map>
#include <iostream>
#include <string>
using namespace std;
int main()
{
string test;
pair<string, string> p = pair<string, string>("Foo", "Bar");
map<pair<string, string>, reference_wrapper<string>> m;
m[make_pair("aa", "bb")] = test;
return 0;
}
A std::reference_wrapper will convert implicitly to a reference to its internal type, but this doesn't work in some contexts, in which case you call .get() for access.
http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper
You can use boost::reference_wrapper to store references in STL containers. Here is your example modified (not tested, and definitely not very well written, just illustrates a point)
#include <map>
#include<iostream>
#include<string>
#include <boost/ref.hpp>
int main()
{
typedef std::pair< std::string, std::string> PairType;
typedef std::map< PairType, boost::reference_wrapper<std::string> > MapType;
std::string test = "Hello there!!";
MapType m;
PairType pp = std::make_pair("aa","bb");
m.insert(std::make_pair(pp , boost::ref(test) ) );
MapType::iterator it (m.find( pp ) );
if(it != m.end())
{
std::cout << it->second.get() << std::endl;
}
//change test
test = "I am different now";
std::cout << it->second.get() << std::endl;
return 0;
}
You cannot use references as the val, due to how the template is built. You could also use pointer instead.
Essentially, the question is if you can use references in containers. Of course, you can, IF you properly prepare your class AND your container. I demonstrate it below with two simple vector containers: vectoref which modifies std::vector<> and the other, vec, which is implemented from scratch.
#include <iostream>
#include <vector>
// requires compilation with --std=c++11 (at least)
using namespace std;
class A {
int _a; // this is our true data
A *_p; // this is to cheat the compiler
public:
A(int n = 0) : _a(n), _p(0)
{ cout << "A constructor (" << this << "," << _a << ")\n"; }
// constructor used by the initializer_list (cheating the compiler)
A(const A& r) : _p(const_cast<A *>(&r))
{ cout << "A copy constructor (" << this << "<-" << &r << ")\n"; }
void print() const {cout << "A instance: " << this << "," << _a << "\n";}
~A() {cout << "A(" << this << "," << _a << ") destructor.\n";}
// just to see what is copied implicitly
A& operator=(const A& r) {
cout << "A instance copied (" << this << "," << _a << ")\n";
_a = r._a; _p = r._p;
return *this;
}
// just in case you want to check if instance is pure or fake
bool is_fake() const {return _p != 0;}
A *ptr() const {return _p;}
};
template<typename T, int sz>
class vec { // vector class using initializer_list of A-references!!
public:
const T *a[sz]; // store as pointers, retrieve as references
// because asignment to a reference causes copy operator to be invoked
int cur;
vec() : cur(0) {}
vec(std::initializer_list<T> l) : cur(0) {
cout << "construct using initializer list.\n";
for (auto& t : l) // expecting fake elements
a[cur++] = t.ptr();
}
const T& operator[](int i) {return *a[i];}
// expecting pure elements
vec& push_back(const T& r) {a[cur++] = &r; return *this;}
void copy_from(vec&& r) {
for (int i = 0; i < r.cur; ++i)
push_back(r[i]);
}
};
template<typename T>
class vectoref : public vector<T *> { // similar to vec but extending std::vector<>
using size_type = typename vector<T*>::size_type;
public:
vectoref() {}
vectoref(std::initializer_list<T> l) {
cout << "construct using initializer list.\n";
for (auto& t : l) // expecting fake elements
vector<T*>::push_back(t.ptr());
}
const T& operator[](size_type i) {return *vector<T*>::at(i);}
// expecting pure elements
vectoref& push_back(const T& r)
{ vector<T*>::push_back(&r); return *this; }
void copy_from(const vectoref&& r) {
for (size_type i = 0; i < r.size(); ++i)
vectoref<T>::push_back(r[i]);
}
};
class X { // user of initializer_list of A
public:
X() {}
void f(initializer_list<A> l) const {
cout << "In f({...}):\n";
for (auto& a : l)
a.ptr()->print();
}
};
int main()
{
A a(7), b(24), c(80);
cout << "----------------------------------\n";
vectoref<A> w{a,a,b,c}; // alternatively, use next line
// vec<A,5> w{a,a,b,c}; // 5-th element undefined
w[0].print();
w[3].print();
cout << "----------------------------------\n";
X x;
x.f({a,b,c,a,b,c,b,a});
cout << "==================================\n";
return 0;
}