Background about code posted: PayRoll is the name of the class. personSalary is a double type variable, and personAge is an integer type variable. The code given is sorting a list by age or by the salary.
struct less_than_salary
{
inline bool operator() (const PayRoll& struct1, const PayRoll& struct2)
{
return (struct1.personSalary < struct2.personSalary);
}
};
struct less_than_age
{
inline bool operator() (const PayRoll& struct1, const PayRoll& struct2)
{
return (struct1.personAge < struct2.personAge);
}
};
I would like some help understanding this section of the given code. I've tried reading what struct is used for and from what I understand, it basically operates as a class and allows you to work with many types of variables at one time. If I'm wrong, what exactly is a struct used for in this context?
Also, I would appreciate it if someone explained what "inline bool operator()" was doing because I've never seen that before and I could not understand by reading the textbook.
Thank you for your help!
Both structs are implementations of a so-called "functor". Given usage like
PayRoll x;
PayRoll y
// initialise x and y
less_than_salary f;
if (f(x,y)) // this calls less_than_salary::operator()(x,y)
std::cout << "X has lower salary than Y\n";
else
std::cout << "X has higher salary than Y\n";
Also, given an array of PayRoll, it is possible to sort
PayRoll a[20];
// initialise elements of a
less_than_salary f;
std::sort(a, a+20, f); // will sort in order of ascending salary
std::sort(a, a+20, less_than_salary()); // does same as preceding two lines
Standard containers (std::vector<PayRoll>, etc) can also be used.
less_than_age allows doing essentially the same thing, but using age rather tnan salary as a sort criterion.
There is no function overloading as such here. Both struct types provide an operator(), but that is not overloading.
A struct like this can be used in the STL function sort, which is avaliable in data structures such as std::list.
#include <list>
#include <iostream>
int main() {
std::list<int> example = {9,8,7,6,5,4,3,2,1};
struct {
bool operator()( const int lhs, const int rhs) {
return lhs < rhs;
}
} compare_list;
example.sort(compare_list);
for(auto &i : example) {
std::cout<<i<<" ";
}
}
Output:
1 2 3 4 5 6 7 8 9
To understand how this works, consider the following:
//..
testS(compare_list);
//..
template <typename T>
void testS(T t) {
std::cout<< t(4,5) << "\n";
std::cout<< t(5,4) << "\n";
}
The output would be
1
0
Related
i'm trying to write a program that Multiplies Cycles to concatenate between permutations and i'm not sure how to define it, also not sure what to use it's either in C++ or Python.
If it's in C++ i should probably use std::string to hold the numbers in a specific order like so:
(1 2 3 ... n)
If it's python i should probably use List to hold everything in it like so:
["(", "1", " ", "2", " ", "3", " ", "...", " ", "n" ")"]
from the user input.
I will also need to search if a number from the right factor exists in the left one
and to define some conditions like if it brought me to the same number i will need to close the cycle product.
(1 2 3 ... n)*(1 2 3 ... k)
But that's where it becomes tricky, how to define the multiplication itself.
I really need some advice here..
C++ allows you to overload the multiplication operator for your own classes. Here is an example (live demo)
#include <iostream>
struct myInt
{
int value;
myInt(int v = 0) : value(v) {};
myInt operator*(const myInt& b) const
{
// complex logic here...
return myInt(value*b.value);
}
};
int main()
{
myInt two(2), three(3), result;
result = two*three;
std::cout << result.value << std::endl;
}
Alternatively you can define the binary operator* outside the class:
myInt operator*(const myInt &a, const myInt& b)
{
return myInt(a*b);
}
However, to implement the complex logic is up you. But this definitely solves your problem.
For your problem, you should not store the numbers as string.
Create a class which holds a vector of integers and define the multiplication operator for this class.
struct myCycle
{
std::vector <unsigned int> data;
myCycle() = default;
myCycle(const std::vector<unsigned int> d) : data(d) {}
myCycle operator*(const myCycle &b) const
{
myCycle result;
// do the multiplication and write to result.
return myCycle;
}
};
Is it possible to define some kind of template that can create a generic comparable operator for structs?
For example is it possible for something like this?
struct A
{
int one;
int two;
int three;
};
bool AreEqual()
{
A a {1,2,3};
A b {1,2,3};
return ComparableStruct<A>(a) == ComparableStruct<A>(b);
}
All this does is a field by field comparison of the structs. You can assume all fields are of basic types or have overloaded operator==.
I have a lot of structs like this and it would save me a lot of time if I can just put it in a template or something for comparison rather than defining an operator== for every single struct. Thanks!
Update
It seems like this is not possible with C++. I wonder why this is voted out of C++ proposals, if anyone has the reasons let us know!
For solution that works with basic types only see solution by R Sahu.
Is it possible to define some kind of template that can create a generic comparable operator for structs?
If the struct has no padding, you can use:
template <typename T>
struct ComparableStruct
{
ComparableStruct(T const& a) : a_(a) {}
bool operator==(ComparableStruct const& rhs) const
{
return (std::memcmp(reinterpret_cast<char const*>(&a_), reinterpret_cast<char const*>(&rhs.a_), sizeof(T)) == 0);
}
T const& a_;
};
Better yet, you can use a function template.
template <typename T>
bool AreEqual(T cost& a, T const& b)
{
return (std::memcmp(reinterpret_cast<char const*>(&a), reinterpret_cast<char const*>(&b), sizeof(T)) == 0);
}
If the struct has any padding, there is no guarantee that use of std::memcmp will work to compare two objects.
Look at https://github.com/apolukhin/magic_get. This library can automagically generate comparison operators for some fairly simple structs.
#include <iostream>
#include <boost/pfr/flat/global_ops.hpp>
struct S {
char c;
int i;
double d;
};
int main() {
S s1{'a', 1, 100.500};
S s2 = s1;
S s3{'a', 2, 100.500};
std::cout << "s1 " << ((s1 == s2) ? "==" : "!=") << " s2\n";
std::cout << "s1 " << ((s1 == s3) ? "==" : "!=") << " s3\n";
}
// Produces
// s1 == s2
// s1 != s3
What you asking to do is to traverse through various struct and comparing members is my understanding.
Iterating over a struct
This seems that it can't be done with standard c++, but that thread gives some ideas on what libraries to use.
It's not clear from your question if all the structs have the same format or not, and I'm assuming they do not.
I have just started learning C++ programming a month or so ago. I am having a great difficulty in ranking and printing out the output based on the ranking. I followed some of the ideas posted in the forum and my code is below. I have no idea of what I have missed and how the code works. What I am trying to do is to sort out the player_data[5] in ascending order based on the attempt field and then sort out again the player_data[5] with time elapsed where the order of the array is based on the attempt and then time elapsed if the attempt is the same. After I sort out the structure of array, I want to cout based on the ranking. Would someone tell me what I am missing and give a brief explanation on the code itself. TIA
#include <algorithm>
using namespace std;
bool player_sorter(player_score const& lhs,player_score const& rhs);
struct player_score
{
char name[31];
int num_attempt;
time_t time_elapsed;
} player_data[5];
bool player_sorter(player_score const& lhs, player_score const& rhs)
{
if (lhs.num_attempt != rhs.num_attempt)
return lhs.num_attempt < rhs.num_attempt;
if (lhs.time_elapsed != rhs.time_elapsed)
return lhs.time_elapsed < rhs.time_elapsed;
}
The std::sort functions works with standard containers, as far as I know it doesn't with C-style array. You should define your type:
typedef struct player_score
{
char name[31];
int num_attempt;
time_t time_elapsed;
} player_score;
And then declare the actual container of your data:
std::vector<player_score> player_data;
Once your filled your container you can sort it with
std::sort(player_data.begin(), player_data.end(), player_sorter);
The function sort is an implementation of a sorting algorithm, maybe a Quicksort. If you call it like this you are telling the function that you want to sort from the beginning to the end, the whole container. The third argument is the function that performs the < comparison and it is fundamental to determine if an element goes before or after the other.
Also, player_sorter must return a value even if the two player_score are equal, you should add a return false to the end of the function, because in that case the first operand is not strictly less than the second but it is equal. In the case the operator were <= you would return true.
std::sort provides "good enough" sorting speed and complexity for most cases. Here is an example using a different struct and std::sort and several ways to sort.
struct foo {
int a;
int *b = nullptr;
bool operator<(const foo & other) const {
return (a < other.a);
}
}
void printvec(const std::vector<foo> & vec) {
for ( foo & f : vec ) {
std::cout << f.a << "\t" << (void*)f.b;
if ( f.b ) {
std::cout << "\t" << *f.b;
}
std::cout << "\n";
}
}
bool sort_foos_on_b(const foo & a, const foo & b) {
// if both are nullptr
if ( (nullptr == a.b) && (nullptr == b.b) ) {
return false;
}
// if one is nullptr
if ( (nullptr == a.b) != (nullptr == b.b) ) {
return (nullptr != a.b);
}
return (*a.b) < (*b.b);
}
void bar() {
const constexpr size_t MAX_FOO = 20;
std::default_random_engine generator;
std::uniform_int_distribution<int> rng(0,MAX_FOO-1);
std::vector<foo> vec(MAX_FOO);
// initialize
for ( foo & f : vec ) {
f.a = rng(generator);
if ( f.a & 1 ) {
f.b = &f.a;
}
}
// uses foo::operator<()
std::sort(vec.begin(), vec.end());
printvec(vec);
// uses lambda
std::sort(vec.begin(), vec.end(), [](const foo & a, const foo & b) -> bool {
// sorting on pointer because why not
return (f.a < f.b);
});
printvec(vec);
// uses explicit sort function
std::sort(vec.begin(), vec.end(), sort_foos_on_b);
printvec(vec);
}
bool foo::operator<() const is used to sort on foo::a.
The lambda is used to sort based on the pointer value of foo::b.
The explicit sort function is used to sort based on the nullness of foo::b and, if both are non-null, then the value pointed to by foo::b.
You can see tradeoffs for each. Using operator< will provide a default method of sorting (not needing to specify a sorting function to std::sort). The lambda allows you to customize your sorting right where you're using it. The explicit sorting function would be used if you need to sort the same way from different locations (instead of repeating the same lambda everywhere).
disclaimer: I wrote above code snippet from memory and don't have access to a compiler at the very moment so it might not compile "as-is"
I want to store a floating point value for an unordered pair of an integers. I am unable to find any kind of easy to understand tutorials for this. E.g for the unordered pair {i,j} I want to store a floating point value f. How do I insert, store and retrieve values like this?
Simple way to handle unordered int pairs is using std::minmax(i,j) to generate std::pair<int,int>. This way you can implement your storage like this:
std::map<std::pair<int,int>,float> storage;
storage[std::minmax(i,j)] = 0.f;
storage[std::minmax(j,i)] = 1.f; //rewrites storage[(i,j)]
Admittedly proper hashing would give you some extra performance, but there is little harm in postponing this kind of optimization.
Here's some indicative code:
#include <iostream>
#include <unordered_map>
#include <utility>
struct Hasher
{
int operator()(const std::pair<int, int>& p) const
{
return p.first ^ (p.second << 7) ^ (p.second >> 3);
}
};
int main()
{
std::unordered_map<std::pair<int,int>, float, Hasher> m =
{ { {1,3}, 2.3 },
{ {2,3}, 4.234 },
{ {3,5}, -2 },
};
// do a lookup
std::cout << m[std::make_pair(2,3)] << '\n';
// add more data
m[std::make_pair(65,73)] = 1.23;
// output everything (unordered)
for (auto& x : m)
std::cout << x.first.first << ',' << x.first.second
<< ' ' << x.second << '\n';
}
Note that it relies on the convention that you store the unordered pairs with the lower number first (if they're not equal). You might find it convenient to write a support function that takes a pair and returns it in that order, so you can use that function when inserting new values in the map and when using a pair as a key for trying to find a value in the map.
Output:
4.234
3,5 -2
1,3 2.3
65,73 1.23
2,3 4.234
See it on ideone.com. If you want to make a better hash function, just hunt down an implementation of hash_combine (or use boost's) - plenty of questions here on SO explaining how to do that for std::pair<>s.
You implement a type UPair with your requirements and overload ::std::hash (which is the rare occasion that you are allowed to implement something in std).
#include <utility>
#include <unordered_map>
template <typename T>
class UPair {
private:
::std::pair<T,T> p;
public:
UPair(T a, T b) : p(::std::min(a,b),::std::max(a,b)) {
}
UPair(::std::pair<T,T> pair) : p(::std::min(pair.first,pair.second),::std::max(pair.first,pair.second)) {
}
friend bool operator==(UPair const& a, UPair const& b) {
return a.p == b.p;
}
operator ::std::pair<T,T>() const {
return p;
}
};
namespace std {
template <typename T>
struct hash<UPair<T>> {
::std::size_t operator()(UPair<T> const& up) const {
return ::std::hash<::std::size_t>()(
::std::hash<T>()(::std::pair<T,T>(up).first)
) ^
::std::hash<T>()(::std::pair<T,T>(up).second);
// the double hash is there to avoid the likely scenario of having the same value in .first and .second, resulinting in always 0
// that would be a problem for the unordered_map's performance
}
};
}
int main() {
::std::unordered_map<UPair<int>,float> um;
um[UPair<int>(3,7)] = 3.14;
um[UPair<int>(8,7)] = 2.71;
return 10*um[::std::make_pair(7,3)]; // correctly returns 31
}
Quite often I have two variables foo1 and foo2 which are numeric types. They represent the bounds of something.
A user supplies values for them, but like a recalcitrant musician, not necessarily in the correct order!
So my code is littered with code like
if (foo2 < foo1){
std::swap(foo2, foo1);
}
Of course, this is an idiomatic sort with two elements not necessarily contiguous in memory. Which makes me wonder: is there a STL one-liner for this?
I suggest to take a step back and let the type system do the job for you: introduce a type like Bounds (or Interval) which takes care of the issue. Something like
template <typename T>
class Interval {
public:
Interval( T start, T end ) : m_start( start ), m_end( end ) {
if ( m_start > m_end ) {
std::swap( m_start, m_end );
}
}
const T &start() const { return m_start; }
const T &end() const { return m_end; }
private:
T m_start, m_end;
};
This not only centralizes the swap-to-sort code, it also helps asserting the correct order very early on so that you don't pass around two elements all the time, which means that you don't even need to check the order so often in the first place.
An alternative approach to avoid the issue is to express the boundaries as a pair of 'start value' and 'length' where the 'length' is an unsigned value.
No, but when you notice you wrote the same code twice it's time to write a function for it:
template<typename T, typename P = std::less<T>>
void swap_if(T& a, T& b, P p = P()) {
if (p(a, b)) {
using std::swap;
swap(a, b);
}
}
std::minmax returns pair of smallest and largest element. Which you can use with std::tie.
#include <algorithm>
#include <tuple>
#include <iostream>
int main()
{
int a = 7;
int b = 5;
std::tie(a, b) = std::minmax({a,b});
std::cout << a << " " << b; // output: 5 7
}
Note that this isn't the same as the if(a < b) std::swap(a,b); version. For example this doesn't work with move-only elements.
if the data type of your value that you're going to compare is not already in c++. You need to overload the comparison operators.
For example, if you want to compare foo1 and foo2
template <class T>
class Foo {
private:
int value; // value
public:
int GetValue() const {
return value;
}
};
bool operator<(const Foo& lhs, const Foo& rhs) {
return (lhs.GetValue() < rhs.GetValue());
}
If your value is some type of int, or double. Then you can use the std::list<>::sort member function.
For example:
std::list<int> integer_list;
int_list.push_back(1);
int_list.push_back(8);
int_list.push_back(9);
int_list.push_back(7);
int_list.sort();
for(std::list<int>::iterator list_iter = int_list.begin(); list_iter != int_list.end(); list_iter++)
{
std::cout<<*list_iter<<endl;
}