I am trying to use the std::set to contain a struct of three member variables.
struct blah{
int a,b,c;
bool operator < ( const blah& blo ) const{
return ( a < blo.a || (a == blo.a && (b != blo.b || c != blo.c ) ) );
}
};
But I keep getting an error that my operator < is invalid. What is wrong with my approach?
struct blah {
int a,b,c;
blah(int aa,int bb,int cc){ a=aa; b=bb; c=cc; }
bool operator < ( const blah& blo ) const{
return ( a < blo.a
|| (a == blo.a && b < blo.b )
|| (a == blo.a && b == blo.b && c < blo.c )
);
}
};
int main() {
std::set<blah> st;
st.insert(blah(1,2,3));
st.insert(blah(1,1,1));
st.insert(blah(1,3,2));
return 0;
}
After altering the code following #paxdiablo code, this worked well. Thanks y'all!
That code compiles fine for me in the following complete program:
#include <iostream>
struct blah {
int a,b,c;
bool operator < ( const blah& blo ) const{
return ( a < blo.a || (a == blo.a && (b != blo.b || c != blo.c ) ) );
}
};
int main (void) {
blah x, y;
x.a=2; x.b=2; x.c=2;
y.a=2; y.b=2; y.c=2;
if (x < y) std::cout << "x<y\n";
if (y < x) std::cout << "x>y\n";
if (!(y < x) && !(x < y)) std::cout << "x=y\n";
return 0;
}
Changing the fields of x and y outputs different messages.
But I see one major problem with the function. It can tell you that both x < y and y < x, in the situation where the two a fields are identical but the b fields differ between the two. If you set both a fields to 1 and set the b fields to 2 and 1, you see:
x<y
y<x
That's not going to end well :-)
The fact that what you're getting is a debug assertion (something specifically built to catch runtime errors in mostly debug code) leads me to believe that the runtime libraries may explicitly be checking for incorrect operator< overloads by detecting that latter case (ie, both x < y and y < x are true).
You should really fix that because it will cause all sorts of problems with collections where (for example) you need to keep things sorted.
By way of example, let's say you wanted to use a, b and c as keys in that priority. A function to do that would contain something like:
// Check primary key.
if (a < blo.a) return true;
if (a > blo.a) return false;
// Primary key equal here, use secondary key.
if (b < blo.b) return true;
if (b > blo.b) return false;
// Primary and secondary keys equal here, use tertiary key.
return (c < blo.c);
Related
Here is a sample Raku code which uses a grep closure with a sample criteria consisting of a function and logical operators:
print "$_, " for ( grep { is-prime $_ and $_ > 5 and not ($_ == 11 or $_ == 13) }, 1..20);
# Output is: 7, 17, 19
I'd like to port this into C++ using a vector or a list and a lambda in which I can set the sample criteria. Here's my take:
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <random>
using namespace std;
bool isPrime(int num){
bool flag=true;
for(int i = 2; i <= num / 2; i++) {
if(num % i == 0) {
flag = false;
break; } }
return flag;}
int main(){
vector<int> a(20);
iota(begin(a), end(a), 1);
vector<int> b(a.size());
auto end = copy_if(cbegin(a), cend(a), begin(b),
[](int x) { return isPrime(x) & ( x > 5 ) & !( (x == 11) | (x == 13) ); });
b.erase(end, b.end());
for (auto n: b)
cout << n << '\n';
// Output is: 7, 17, 19
}
I'm trying yo make it without using copy_if i.e. by using find_if so that I don't have to create the auxiliary vector b Here is my take:
for (auto x : a) cout << find_if(a.begin(), a.end(), [x](){return isPrime(x) & ( x > 5 ) & !( (x == 11) | (x == 13));});
But it doesn't work. How to do it?
What Perl calls grep here is called a filter in C++ (and some other contexts). C++20 offers std::ranges::filter_view for the purpose, but since this question is tagged for C++17…
The find_if approach you tried doesn't make sense because it still involves iterating over every element (and asks the library to search with a "predicate" that takes no arguments).
It's certainly possible to use std::find_if to advance an iterator to each new value of interest in turn:
for(auto i=a.begin(),e=a.end();i!=e;
i=find_if(i+1,e,[](int x)
{return isPrime(x) & ( x > 5 ) && !( (x == 11) || (x == 13));})
cout << n << '\n';
(Note the logical instead of bitwise operators.) Since this involves an explicit loop (to keep track of the start position for each search) it's no better than the purely imperative approach of considering every element:
for(auto n : a)
if(isPrime(x) && ( x > 5 ) && !( (x == 11) || (x == 13) ))
cout << n << '\n';
Perhaps the most natural approach to avoiding the copy prior to C++20 is to use std::copy_if but with a stream iterator as the output "sequence":
copy_if(cbegin(a), cend(a), std::ostream_iterator<int>(std::cout, "\n"),
[](int x) { return isPrime(x) && ( x > 5 ) && !( (x == 11) || (x == 13) ); });
Prior to C++20, there isn't a std equivalent of 1..20 usable with for ( : ), you would instead use for (;;).
auto criteria = [](int x){ return isPrime(x) && (x > 5) && (x != 11) && (x != 13); };
for (int x = 1; x <= 20; ++x) {
if (criteria(x)) {
std::cout << x;
}
}
Trying to implement a combination of 4 objects taken 2 at a time without taking into account the arrangement (such must be considered duplicates: so that order is not important) of objects with std::set container:
struct Combination {
int m;
int n;
Combination(const int m, const int n):m(m),n(n){}
};
const auto operator<(const auto & a, const auto & b) {
//explicitly "telling" that order should not matter:
if ( a.m == b.n && a.n == b.m ) return false;
//the case "a.m == b.m && a.n == b.n" will result in false here too:
return a.m == b.m ? a.n < b.n : a.m < b.m;
}
#include <set>
#include <iostream>
int main() {
std::set< Combination > c;
for ( short m = 0; m < 4; ++ m ) {
for ( short n = 0; n < 4; ++ n ) {
if ( n == m ) continue;
c.emplace( m, n );
} }
std::cout << c.size() << std::endl; //12 (but must be 6)
}
The expected set of combinations is 0 1, 0 2, 0 3, 1 2, 1 3, 2 3 which is 6 of those, but resulting c.size() == 12. Also, my operator<(Combination,Combination) does satisfy !comp(a, b) && !comp(b, a) means elements are equal requirement.
What am I missing?
Your code can't work1, because your operator< does not introduce a strict total ordering. One requirement for a strict total ordering is that, for any three elements a, b and c
a < b
and
b < c
imply that
a < c
(in a mathematical sense). Let's check that. If we take
Combination a(1, 3);
Combination b(1, 4);
Combination c(3, 1);
you see that
a < b => true
b < c => true
but
a < c => false
If you can't order the elements you can't use std::set. A std::unordered_set seems to more suited for the task. You just need a operator== to compare for equality, which is trivial and a hash function that returns the same value for elements that are considere identical. It could be as simple as adding m and n.
1 Well, maybe it could work, or not, or both, it's undefined behaviour.
Attached is the working code. The tricky part that you were missing was not adding a section of code to iterate through the already working set to then check the values. You were close! If you need a more thorough answer I will answer questions in the comments. Hope this helps!
#include <set>
#include <iostream>
using namespace std;
struct Combination {
int m;
int n;
Combination(const int m, const int n):m(m),n(n){}
};
const auto operator<(const auto & a, const auto & b) {
//explicitly "telling" that order should not matter:
if ( a.m == b.n && a.n == b.m ) return false;
//the case "a.m == b.m && a.n == b.n" will result in false here too:
return a.m == b.m ? a.n < b.n : a.m < b.m;
}
int main() {
set< Combination > c;
for ( short m = 0; m < 4; ++ m )
{
for ( short n = 0; n < 4; ++ n )
{
//Values are the same we do not add to the set
if(m == n){
continue;
}
else{
Combination s(n,m);
const bool is_in = c.find(s) != c.end();
if(is_in == true){
continue;
}
else{
cout << " M: " << m << " N: " << n << endl;
c.emplace( m, n);
}
}
}
}
cout << c.size() << endl; //16 (but must be 6)
}
I have following struct,
struct cube
{
int index l , b , h;
bool operator<(const cube & c2) const
{
if (l == c2.l && b == c2.b && h == c2.h)
return index < c2.index;
if (l == c2.l && b == c2.b)
return h < c2.h;
if (l == c2.l )
return b < c2.b;
return l < c2.l;
}
bool operator==(const cube c2)
{
return index != c2.index && l == c2.l && b == c2.b;
}
};
Now I want to apply upper_bound on vector of this struct as per condition in == operator.
However , it is still returning me those iterators where index are same
int pos2 = upper_bound(v.begin(),v.end(),v[i]) - v.begin();
i.e v[i].index is equal to v[pos2].index
It's possible that for two cube instances foo and bar that foo < bar is true when foo == bar is also true. You could fix that by writing index == c2.index && l == c2.l && b == c2.b as the returned expression in operator==.
This contradiction is the root cause of your issues, although note that std::upper_bound does itself only require that operator< is implemented appropriately; which yours is.
Isn't index more of a property of a collection of cubes rather than a given cube? That is, it shouldn't appear in the cube class?
How c++ set/map checks the equality of keys ?
for example in this example :
struct A
{
int id , val;
A( int _val = 0 , int _id = 0 )
{ val = _val , id = _id; }
bool friend operator < ( const A &x , const A &y )
{
return x.val < y.val;
}
};
set< A > s;
because we haven't written the == operator ?
it checks if (!(x < y) && !(y < x))
operator== is not used by std::set. Elements a and b are considered equal iff !(a < b) && !(b < a)
Note: A set is probably inappropriate if you define equality in a different sense than ordering. Equality in set essentially means the two element will have the same place in sorted sequence of items.
I'm using a custom comparer function for my next_permutation function, but I don't understand why I'm getting the error:
Expression: invalid operator<
I want my function to work with at least these restrictions in the function body, but keep getting errors:
bool mycomp(int i, int j)
{
return (((i < 0) && (j > 0)) || ((i > 0) && (j < 0)));
};
but when I do it like this, it works fine:
bool mycomp(int i, int j)
{
return (((i < 0) && (j > 0)));
};
I want to also add another restriction, but don't know how.
here is the relevant code with the next_permutation function:
int counter, size, *guests;
for (int i = 2; i <= 9; i++)
{
size = i * 2;
counter = 1;
guests = new int[size];
for (int j = 0; j < size; j += 2)
{
guests[j] = counter;
guests[j + 1] = 0 - counter;
++counter;
}
sort(guests, guests + size);
counter = 0;
while (next_permutation(guests, guests + size, mycomp))
{
++counter;
}
}
I also understand that there is a strict weak ordering requirement. I understood the gist of it after reading about it, but not sure exactly how it applies to this situation. Thank you in advance.
Your compiler is trying to tell you (through a runtime assertion) that your comparator is invalid. It is invalid as it does not respect the strict weak ordering contract for at least two reasons :
1) It is not antisymmetric ( ie f(x, y) implies !f(y, x)) :
std::cout << mycomp(2, -3) << '\n';
std::cout << mycomp(-3, 2) << '\n';
Output:
true
true
2) It is not transitive (ie f(x, y) and f(y, z) imply f(x, z) ):
std::cout << mycomp(2, -3) << '\n';
std::cout << mycomp(-3, 2) << '\n';
std::cout << mycomp(2, 2) << '\n';
Output:
true
true
false
false
Demo
Your probably need to rethink your problem, and how you really want to order your elements while doing the permutations.
Permutation is about ordering. The default ordering is 1<2<3<4<5<6 etc -- the order you are used to.
The custom comparator lets you set an ordering that is different than the default one.
This is useful in a number of situations.
For a toy example, you could set all even numbers to be greater than all odd ones -- 1<3<5<7<...<0<2<4<6<8<....
Example implementation that leverages std::tuple:
std::tuple<bool,unsigned> myhelper( unsigned x ) {
return std::make_tuple( !(x%2), x );
}
bool myorder( unsigned lhs, unsigned rhs ) {
return helper(lhx)<helper(rhs);
}
For types that don't have an operator < that is a strict weak ordering, it lets you provide one. A complex number can be ordered lexicographically, or magnitude-wise: the magnitude-ordering is not a strict weak ordering, and having < be a lexicographic order would be surprising.