I have a type struct Type_Specifier which I want to represent an immutable tree like structure that I can compare. I have the following code to illustrate what I want:
#include <vector>
struct Parameter_Specifier;
struct Type_Specifier
{
explicit Type_Specifier(void* tag = nullptr, std::vector<Parameter_Specifier> parameters = {})
: tag(tag), parameters(parameters) { }
Type_Specifier(const Type_Specifier&) = default;
Type_Specifier(Type_Specifier&&) = default;
Type_Specifier& operator=(const Type_Specifier&) = default;
Type_Specifier& operator=(Type_Specifier&&) = default;
~Type_Specifier() = default;
private:
// Points to an arbitray memory location (whose lifetime is not managed by this type)
void* tag;
std::vector<Parameter_Specifier> parameters;
public:
static bool operator ==(const Type_Specifier& left, const Type_Specifier& right)
{
if (left.tag != right.tag)
return false;
else if (left.parameters.size() != right.parameters.size())
return false;
else for (std::size_t i = 0; i < left.parameters.size(); i++)
{
if (!(left.parameters[i] == right.parameters[i]))
return false;
}
return true;
}
static bool operator <(const Type_Specifier& left, const Type_Specifier& right)
{
if (left.tag < right.tag)
return true;
else if (left.parameters.size() < right.parameters.size())
return true;
else if (left.parameters.size() > right.parameters.size())
return false;
else for (std::size_t i = 0; i < left.parameters.size(); i++)
{
if (left.parameters[i] < right.parameters[i])
return true;
else if (right.parameters[i] < left.parameters[i])
return false;
}
return false; // left == right
}
};
struct Parameter_Specifier
{
explicit Parameter_Specifier(Type_Specifier type = Type_Specifier(), std::vector<char> value = {})
: type(type), value(value) { }
Parameter_Specifier(const Parameter_Specifier&) = default;
Parameter_Specifier(Parameter_Specifier&&) = default;
Parameter_Specifier& operator=(const Parameter_Specifier&) = default;
Parameter_Specifier& operator=(Parameter_Specifier&&) = default;
~Parameter_Specifier() = default;
private:
Type_Specifier type;
// Arbitrary data (not a 'string' or sequence of 'characters')
std::vector<char> value;
public:
static bool operator ==(const Parameter_Specifier& left, const Parameter_Specifier& right)
{
if (!(left.type == right.type))
return false;
else if (left.value.size() != right.value.size())
return false;
else for (std::size_t i = 0; i < left.value.size(); i++)
{
if (left.value[i] != right.value[i])
return false;
}
return true; // left == right
}
static bool operator <(const Parameter_Specifier& left, const Parameter_Specifier& right)
{
if (left.type < right.type)
return true;
else if (left.value.size() < right.value.size())
return true;
else if (left.value.size() > right.value.size())
return false;
else for (std::size_t i = 0; i < left.value.size(); i++)
{
if (left.value[i] < right.value[i])
return true;
else if (left.value[i] > right.value[i])
return false;
}
return false; // left == right
}
};
However it of course will not compile, probably due to struct Parameter_Specifier being incomplete, and the operators '<', and '==' being undefined.
My question is this:
How Can I modify the above types to make it compile and work properly? (and efficiently?)
Notes:
The purpose of this type is to be used in a two-way map structure to be able to map between struct Type_Specifier and std::size_t (i.e. it needs to be both a key type and a value type)
I will define the other comparison operators '!=', '>', '>=' and '<=' based on my definitions of '==' and '>', I have omitted them here for brevity
I am pretty sure that a == b is equivalent to !(a < b) && !(b < a)
The order of comparison isn't really relevant, as look as it is a strict weak ordering relation
I would like the Type_Specifier type to follow the RAII idiom
The Paramater_Specifier type is only used for children of Type_Specifier and nowhere else
I only whish to use the C++ standard library
I was considering making Type_Specifier::parameters an std::vector<Parameter_Specifier*> but then I would have to manually manage allocation, in Type_Specifier, which would cause obvious problems as Parameter_Specifier is incomplete there.
You have problems with relational operators.
1) for operator==(), you have two alternative: (a) a method of the class/struct with one parameter (the left argument, *this, is implicit), that can't be static, or (b) an external function (friend, usually) with two parameter. You have mixed the two alternative and have made the method static. I strongly suggest alternative (b): external function. So (take in count that there is an operator==() for std::vector) operator==() for Type_Specifier could be
friend bool operator== (const Type_Specifier & left,
const Type_Specifier & right)
{
return ( left.tag == right.tag )
&& ( left.parameters == right.parameters );
}
For Parameter_Specifier could be
friend bool operator== (const Parameter_Specifier & left,
const Parameter_Specifier & right)
{
return ( left.type == right.type )
&& ( left.value == right.value );
}
2) same thing for operator<(). Moreover, if "left.tag < rigth.tag" is false, I think you should verify that isn't true that left.tag > right.tag. My suggestion (but with different behaviour; caution) for Type_Specifier is
friend bool operator< (const Type_Specifier & left,
const Type_Specifier & right)
{
return ( left.tag < right.tag )
|| ( ( left.tag == right.tag )
&& ( left.parameters < right.parameters ) );
}
For Parameter_Specifier, my suggestion (with different behaviour; caution) is
friend bool operator< (const Parameter_Specifier & left,
const Parameter_Specifier & right)
{
return ( left.type < right.type )
|| ( (left.type == right.type )
&& (left.value < right.value ) );
}
3) having operator==() and operator<() others relational operator are straightforward. For Type_Specifier
friend bool operator!= (const Type_Specifier & left,
const Type_Specifier & right)
{ return ! (left == right); }
friend bool operator> (const Type_Specifier & left,
const Type_Specifier & right)
{ return (right < left); }
friend bool operator<= (const Type_Specifier & left,
const Type_Specifier & right)
{ return ! (right < left); }
friend bool operator>= (const Type_Specifier & left,
const Type_Specifier & right)
{ return ! (left < right); }
For Parameter_Specifier... well... change Type_Specifier with Parameter_Specifier.
P.s.: sorry for my bad English.
Related
I'm testing std::set with a custom comparator. But I see the same object getting inserted twice.
Following is the object class:
class Info
{
public:
Info(string n, string oN, int dom):
name(n),
objName(oN),
domain(dom)
{}
void setName(std::string n) { name = n;}
void setObjName(std::string n) { objName = n;}
void setDomain(int dom) { domain = dom; }
std::string getName() const { return name;}
std::string getObjName() const { return objName;}
int getDomain() const { return domain;}
private:
std::string name;
std::string objName;
int domain;
};
Following is my custom comparator:
struct InfoCmp {
bool operator() (const Info &lhs, const Info &rhs) const {
if((lhs.getName() < rhs.getName()) || (lhs.getObjName() < rhs.getObjName()) || (lhs.getDomain() < rhs.getDomain()) ){
return true;
}
return false;
}
};
Following is the usage:
Info rst1("rst1", "rstObj1", 1);
Info rst2("rst2", "rstObj2", 2);
Info rst3("rst1", "rstObj3", 3);
std::set<Info,InfoCmp> resetSet;
resetSet.insert(rst1);
resetSet.insert(rst2);
resetSet.insert(rst3);
resetSet.insert(rst1);
resetSet.insert(rst2);
resetSet.insert(rst3);
I see rst2 inserted twice, but it shouldn't be as per my comparator.
I see that you've come up with your own solution, after recognizing from the comments that your original did not impose a strict object ordering as required by set. Here's a different version that only requires operator< and not operator==, making it consistent with the classes and algorithms of the standard library. It also simplifies things if you're e.g. doing a case insensitive comparison.
struct InfoCmp {
bool operator() (const Info &lhs, const Info &rhs) const {
if(lhs.getName() < rhs.getName())
return true;
if(rhs.getName() < lhs.getName())
return false;
if(lhs.getObjName() < rhs.getObjName())
return true;
if(rhs.getObjName() < lhs.getObjName())
return false;
return lhs.getDomain() < rhs.getDomain();
}
};
struct InfoCmp2 {
bool operator() (const Info &lhs, const Info &rhs) const {
return std::make_tuple(lhs.getName(), lhs.getObjName(), lhs.getDomain()) < std::make_tuple(rhs.getName(), rhs.getObjName(), rhs.getDomain());
}
};
This operator can written with make_tuple as well, and working fine.
as suggested by Cory Kramer, adding strict ordering in comparator fixed the problem.
struct InfoCmp {
bool operator() (const Info &lhs, const Info &rhs) const {
if((lhs.getName() < rhs.getName())
|| ( (lhs.getName() == rhs.getName()) && (lhs.getObjName() < rhs.getObjName()) )
|| ( (lhs.getName() == rhs.getName()) && (lhs.getObjName() == rhs.getObjName()) && (lhs.getDomain() < rhs.getDomain()) )){
return true;
}
return false;
}
};
I'm overloading both == and != operators and want the latter to refer to the former in order to not repeat any code at all. This is what I have written:
bool Date :: operator == (const Date & other) const {
bool are_equal = Year() == other.Year();
for (int i=0; i<other.NumEvents() && are_equal; i++)
are_equal = this[i] == other[i];
return are_equal;
}
bool Date :: operator != (const Date & other) const {
return !(this == other);
}
The big problem here is that this is not a Date but a Date*. Is there a way to refer to this Date without a pointer or using this along with other Date?
Dereference the pointer:
return !(*this == other);
You need to dereference the this pointer in order to invoke your operators on the Date object it refers to, eg:
bool Date :: operator == (const Date & other) const {
bool are_equal = ((Year() == other.Year()) && (NumEvents() == other.NumEvents()));
for (int i = 0; (i < other.NumEvents()) && are_equal; ++i) {
are_equal = ((*this)[i] == other[i]);
}
return are_equal;
}
bool Date :: operator != (const Date & other) const {
return !((*this) == other);
}
You can try to overload != function like this:
bool Date :: operator != (const Date & other) const {
return !(*this == other);
}
I have
struct data_cell{
int owner;
std::vector<int> shares;
};
data_cell** data; //grid, very big
std::map<data_cell, data_cell*> cells; //all uniq cells
bool operator==(const data_cell &b, const data_cell &o) {
return (b.owner == o.owner && b.shares == o.shares);
}
bool operator<(const data_cell &b, const data_cell &o) {
return (b.owner < o.owner && b.shares != o.shares);
}
int to_grid(float, float);
sometimes when I do:
for (int i=0;i<ids.size();i++){//example
data_cell o=ids[i];
//some work where fills o.shares
data[to_grid(x,y)]=(cells[o]?:cells[o]=(new data_cell(o)));
printf("%d |%d|%d|%d %d\n", s.id, o.owner, cells[o]->owner, data[to_grid(x,y)]->owner, to_grid(x,y));
}
I got that o.owner != cells[o]->owner, (printf shows different values), and if I print cells[o] before assign it returns nonzero pointer, but this key doesn't exist in map.
I use gcc-4.6.
what is wrong with this code?
Add1:
Change to
bool operator<(const data_cell &b, const data_cell &o) {
return (b.owner < o.owner && b.shares < o.shares);
}
doesn't help, but
bool operator<(const data_cell &b, const data_cell &o) {
return (b.owner < o.owner && b.shares <= o.shares);
}
Add2:
The last version that works(I hope):
bool operator<(const data_cell &b, const data_cell &o) {
return (b.owner < o.owner && b.shares <= o.shares) ||
(b.owner <= o.owner && b.shares < o.shares);
}
May be some additions?
Your == and < operators have to be such that exactly one of a < b, a == b, and b < a is true, and the other two false. That is not the case here due to b.shares != o.shares in your implementation of operator<.
I have a little problem, which the Visual Studio compiler don't seem to be bothered by, but do in eclipse and g++.
I have 2 classes, Card and CardDeck. I do have the operator <= for 2 Carddecks as parameters and do not for 2 cards. I have a converting ctor, which converts Card to a Deck.
So the problem is when I do:
card1 <= card2
Which does work fine in visual, as it converts the left part to deck, then converts the right and then does the comparing.
In g++ it says:
no match for 'operator<=' (operand types are 'Card' and 'Card')
But there shouldn't be one. as I said I want the convert ctor would convet both sides and do the compare?
Any explanation and solution for this ?
Edit(the operator and ctor declaration and code):
CardDeck(const Card&);
friend bool operator<=(const CardDeck&, const CardDeck&);
CardDeck::CardDeck(const Card& card){
_Deck.push_back(card);
}
Here's a quick&dirty of what I think you are trying to do:
#include <iostream>
struct Element
{
int x;
Element() :x(0) {}
virtual ~Element() {}
};
struct Container
{
Element elems[10];
int n;
Container() { n=0; }
Container(const Element &e) { elems[0]=e; n=1; }
virtual ~Container() {}
friend bool operator<=(const Container &l, const Container &r);
};
bool operator<=(const Container &l, const Container &r)
{
if (l.n<=r.n) { std::cout << "less/equ\n"; return true; }
else { std::cout << "greater\n"; return false; }
}
int main(int argc, const char *argv[])
{
//Container container;
Element a, b;
if (a<=b) std::cout << "okay\n"; else std::cout << "fail\n";
return 0;
}
It works fine with gcc.
Deck(card1) <= Deck(card2)
Make operator<= (operand types are 'Card' and 'Card') override cause you use(mean) that in card1 <= card2 expression. Your code have architecture errors.
Update:
struct Card
{
bool operator<( const Card& right ) const;
};
struct Deck
{
std::vector<Card> m_arr;
Deck() = default;
Deck( const Card& conversion )
{
m_arr.push_back( conversion );
}
bool operator<( const Deck& right ) const;
};
bool Card::operator<( const Card& right ) const
{
return false;
}
bool Deck::operator<( const Deck& right ) const
{
bool result = false;
if(m_arr.size() < right.m_arr.size())
{
result = true;
}
else if(!m_arr.empty() && m_arr.size() == right.m_arr.size() )
{
result = true;
std::vector<Card>::const_iterator it = right.m_arr.begin();
std::vector<Card>::const_iterator it2 = m_arr.begin();
for(; it2 != m_arr.end(); ++it, ++it2 )
{
if((*it) < (*it2))
{
result = false;
break;
}
}
}
return result;
}
I have a set of tuples of 3 integers and I don't want any duplicates. That is, I don't want 2 entries with the same 3 values.
And here is my code.
struct Key{
unsigned a;
unsigned b;
unsigned c;
public:
Key(unsigned _a, unsigned _b, unsigned _c) :
a(_a),
b(_b),
c(_c) {}
bool operator<(const Key& rhs) const
{
if (a < rhs.a) {
return true;
}
if (b < rhs.b) {
return true;
}
if (c < rhs.c) {
return true;
}
return false;
};
};
std::set<Key> myset;
But I see duplicates in myset sometimes. I can't catch exactly what sequence causes the duplicate entry to be added. It doesn't always happen.
My question is this, is there something intrinsically wrong with my operator< function?
It's nearly right! But you are cascading too soon.
bool operator<(const Key& rhs) const
{
if (a < rhs.a)
return true;
if (a > rhs.a)
return false;
if (b < rhs.b)
return true;
if (b > rhs.b)
return false;
return (c < rhs.c);
};
Otherwise the following, for example, gives the wrong result:
Key lhs{3,1,0};
Key rhs{2,2,0};
assert(lhs < rhs); // passes, wrongly, because !(3 < 2) but then (1 < 2).
// you need an immediate `return false` when !(3 < 2)
It is safer to do something like this:
bool operator<(const Key& rhs) const
{
return std::tie(a, b, c) < std::tie(rhs.a, rhs.b, rhs.c);
}
C++'s standard library already knows what to do with that, so you don't have to.
Now, how can your bug lead to duplicate keys in a set?
Set's internal algorithm relies on the ordering being a strict weak ordering — when you break that precondition, you break the algorithms managing the internal tree, which is constructed and arranged using this ordering as its bible.
All hell breaks loose, basically. You could get a crash from this. In your case the symptoms were somewhat more benign (at least for now), with a deformed/mangled data tree resulting in the appearance of duplicated data.
It's folly to try to reason about the specific chain of events that led to a specific outcome, if you started off by breaking the preconditions and causing UB.
https://stackoverflow.com/a/979768/560648
Implementing comparison operators via 'tuple' and 'tie', a good idea?
Your operator<() is not consistent, as key1<key2 and key2<key1 could both be true (example: key1={1,3,0}, key2={3,1,0}). You should give the member variables a precedence in comparison:
if (a < rhs.a) {
return true;
} else if (a == rhs.a) {
if (b < rhs.b) {
return true;
} else if (b == rhs.b) {
if (c < rhs.c) {
return true;
}
}
}
return false;
You could indeed use standard class std::tuple as the key.
Nevertheless the operator can be defined the following way
bool operator <( const Key &rhs ) const
{
return
( a < rhs.a ) ||
( !( rhs.a < a ) && ( b < rhs.b ) ) ||
( !( rhs.a < a ) && !( rhs.b < b ) && ( c < rhs.c ) );
};
That this operator would work all you need is that for the type of objects a, b, and c there would be defined operator < Of course for arithmetic types it is already defined.
In fact it is the same as
#include <tuple>
//...
bool operator <( const Key &rhs ) const
{
return std::tie( a, b, c ) < std::tie( rhs.a, rhs.b, rhs.c );
}