The following code doesn't work with MSVC++ 2019, but it works on GCC compiler.
#include <set>
#include <string>
#include <iostream>
struct MyData {
MyData() {}
MyData(std::string keyA, std::string keyB) :keyA(keyA), keyB(keyB) {}
std::string keyA;
std::string keyB;
};
struct Compare {
bool operator() (const MyData& lhs, const MyData& rhs) const
{
if (lhs.keyA < rhs.keyA)
return true;
if (lhs.keyB < rhs.keyB)
return true;
else
// All else conditions would be false
return false;
}
};
int main()
{
std::set<MyData, Compare> s;
s.insert(MyData("Clark", "Alice"));
s.insert(MyData("Bob", "Alice"));
s.insert(MyData("Alice", "Bob"));
s.insert(MyData("Derek", "Clark"));
for (auto& i : s)
{
std::cout << i.keyA << ", " << i.keyB << std::endl;
}
}
Here's the error on MSVC:
While on GCC, it shows this output:
Alice, Bob
Bob, Alice
Clark, Alice
Derek, Clark
Process returned 0 (0x0) execution time : 0.971 s
Press any key to continue.
What's causing this error, and how to define the operator correctly?
Compare::operator() is not correct. It does not meet the criteria of strict weak ordering.
You can change it to:
bool operator() (const MyData& lhs, const MyData& rhs) const
{
if (lhs.keyA != rhs.keyA)
return (lhs.keyA < rhs.keyA)
return (lhs.keyB < rhs.keyB);
}
It can be further simplified using std::tie.
bool operator() (const MyData& lhs, const MyData& rhs) const
{
return std::tie(lhs.keyA, lhs.keyB) < std::tie(rhs.keyA, rhs.keyB);
}
Your comparator fails to adhere to the rules for strict weak ordering, that is to say it is possible to have the following situation:
a < b
b < c
a >= c
MSVC has evidently detected this and raised an assertion failure.
To fix this, you can change your comparator as follows:
bool operator() (const MyData& lhs, const MyData& rhs) const
{
if (lhs.keyA < rhs.keyA)
return true;
if (lhs.keyA == rhs.keyA && lhs.keyB < rhs.keyB)
return true;
return false;
}
Or, more compactly, use std::tie:
bool operator() (const MyData& lhs, const MyData& rhs) const
{
return std::tie (lhs.keyA, lhs.keyB) < std::tie (rhs.keyA, rhs.keyB);
}
I also got rid of the redundant else.
Related
I'm trying to understand how comparator works for priority queue, I did several tests:
Test 1: Create comparator class, and use priority_queue<T, vector<T>, cmp>
It always works fine
Test 2:
struct test {
int a = 0;
};
bool operator<(const test& lhs, const test& rhs) {
return lhs.a < rhs.a;
}
int main()
{
priority_queue<test> pq;
}
This works as expected.
Test 3: Put test 2 inside a class
class T{
struct test {
int a = 0;
};
bool operator<(const test& lhs, const test& rhs) {
return lhs.a < rhs.a;
}
};
Compiling Error:
'bool T::operator<(const T::test&, const T::test&)' must take exactly one argument
It seems that the compiler thought that I was overloading the operator < for class T. Is there any other ways to accomplish this if I really need the classes to be nested?
Test 4: Overload operator<
struct test {
int a = 0;
bool operator<(const test& rhs) {
return a < rhs.a;
}
};
int main()
{
vector<test> v;
sort(v.begin(), v.end()); // No error
set<test> s; // No error
priority_queue<test> pq; // Compiling error
}
Only priority_queue throws an error:
'passing 'const test*' as 'this' argument discards qualifiers'
I don't know why this works for sort and set but not for priority queue.
The operator< must take two arguments while when you make it a member of class T, it implicitly gets this as the third argument, thus the error in Test 3:
class T {
bool operator<(const test& lhs, const test& rhs) { // error
return lhs.a < rhs.a;
}
};
To fix this, define operator< in the nested test class:
class T {
public:
struct test {
int a = 0;
friend bool operator<(const test& lhs, const test& rhs) {
return lhs.a < rhs.a;
}
};
};
or at the namespace scope.
And in Test 4 operator< should be const:
struct test {
int a = 0;
bool operator<(const test& rhs) const {
return a < rhs.a;
}
};
A comparator for a priority_queue works as for any other comparator. It should be able to participate in the expression
if (compare(element1, element2))
The default implementation reduces to
if (element1 < element 2) ....
What you did in 3 resulted in
if ( tObject.operator<(anotherTObject, yetAnotherTObject))
So your
bool T::operator<(const test& lhs, const test& rhs) {
return lhs.a < rhs.a;
}
should really be comparing to the object itself like
bool T::operator<(const test& rhs) const {
auto lhs = *this;
return lhs.a < rhs.a;
}
The last const acts like the const on the first argument in vitauts reasonable answer.
I am trying to use std::find with two different types but providing the necessary boolean operators.
class FooDetails
{
public:
FooDetails(int size = 5)
: m_size(size) { /* empty */ }
bool operator<(const FooDetails& other) const { return m_size < other.m_size; }
bool operator==(const FooDetails& other) const { return m_size == other.m_size; }
private:
int m_size;
};
class Foo
{
public:
Foo(int size)
: m_details(size) { /* empty */}
bool operator==(const Foo& other) const { return m_details == other.m_details; }
bool operator==(const FooDetails& other) const {return m_details == other; }
bool operator<(const Foo& other) const { return m_details < other.m_details; }
bool operator<(const FooDetails& other) const { return m_details < other; }
FooDetails m_details;
};
bool operator==(const FooDetails& lhs, const Foo& rhs) { return lhs == rhs.m_details; }
bool operator==(const Foo& lhs, const FooDetails& rhs) {return lhs.m_details == rhs; }
bool operator<(const FooDetails& lhs, const Foo& rhs) { return lhs < rhs.m_details; }
bool operator<(const Foo& lhs, const FooDetails& rhs) { return lhs.m_details < rhs; }
int main() {
std::vector<Foo> haystack = { FooDetails(5), FooDetails(6), FooDetails(7) };
FooDetails needle(6);
std::find(haystack.begin(), haystack.end(), needle);
return 0;
}
Since std::find uses operator== I would expect this to work, since all necessary functions are provided. However this does not compile. Why is that and how do I fix it?
I know I could use std::find_if, but I assume that's a bit slower and even if it's not I'd like to know why std::find doesn't work.
I've changed your code to this:
#include <algorithm>
class FooDetails
{
public:
FooDetails(int size = 5)
: m_size(size) { /* empty */ }
bool operator<(const FooDetails& other) const { return m_size < other.m_size; }
bool operator==(const FooDetails& other) const { return m_size == other.m_size; }
private:
int m_size;
};
class Foo
{
public:
Foo(int size)
: m_details(size) { /* empty */}
FooDetails m_details;
};
bool operator==(const FooDetails& lhs, const Foo& rhs) { return lhs == rhs.m_details; }
bool operator==(const Foo& lhs, const FooDetails& rhs) {return lhs.m_details == rhs; }
bool operator<(const FooDetails& lhs, const Foo& rhs) { return lhs < rhs.m_details; }
bool operator<(const Foo& lhs, const FooDetails& rhs) { return lhs.m_details < rhs; }
int main() {
std::vector<Foo> haystack = { Foo(5), Foo(6), Foo(7) };
FooDetails needle(6);
std::find(haystack.begin(), haystack.end(), needle);
return 0;
}
And it successfully compiles. Your original version contains two errors:
You try to create std::vector<Foo> initialising it with std::initialization_list<FooDetails>.
You have provided too many comparison operators: both free versions and members of the Foo struct. So during lookup compiler complains that it doesn't know which one of them to choose. You should leave only one of them.
You have no such constructor in Foo class which constructs it from FooDetails. You need to define the following and your code will work:
Foo(const FooDetails& d) : m_details(d) {}
I need my container to contain unique elements only, so I have a structure like this:
class OD
{
private:
std::string key;
public:
OD(){}
OD(const WayPoint &origin, const WayPoint &destination):
origin(origin), destination(destination)
{
std::stringstream str("");
str << origin.node_->getID() << "," << destination.node_->getID();
key = str.str();
}
bool operator<(const OD & rhs) const
{
return key < rhs.key;
}
bool operator()(const OD & rhs, const OD & lhs)
{
return rhs < lhs;
}
};
and a container :
std::set<OD,OD> t;
now I need to change my container to boost::unordered_set type, do I need to modify the functor? I am confused because I know I can't separate order and uniqueness implementation and this time the container is not ordered . So I fear my operator() overload would be useless.
Here's an example of defining custom hash and comparison operators for an unordered_set:
#include <iostream>
#include <functional>
#include <unordered_set>
struct X
{
std::string key_;
};
int main() {
std::unordered_set<X,
std::function<size_t(const X&)>,
std::function<bool(const X&, const X&)> > s{
5, // initial bucket count
[](const X& x) { return std::hash<decltype(x.key_)>()(x.key_); },
[](const X& lhs, const X& rhs) { return lhs.key_ == rhs.key_; }
};
s.insert({"one"});
s.insert({"two"});
s.insert({"three"});
for (auto& x : s)
std::cout << x.key_ << '\n';
}
See it run here.
as you can see from the code I want to overload the < operator twice. 1 to sort by dist and the other by nodeID. I would like to check if there is any way to call the different overloaded methods. For example in the compLoc method, when I use the sort() method I want it to be sorted by nodeID but in other methods I want it to be sorted by dist.
struct AttSet{
int nodeID;
double dist;
bool operator < (const AttSet & str) const{
return (dist < str.dist);
}
/*
bool operator <(const AttSet & str){
return (nodeID < str.nodeID);
*/
bool operator == (const AttSet & str){
return nodeID == str.nodeID;
}};
void compLoc(Edge *edge, vector<Node*> &vertices){
int l = edge->length;
int vl = edge->head->nodeID;
int vr = edge->tail->nodeID;
/*
sort(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end());
sort(vertices[vr]->attSet.begin(), vertices[vr]->attSet.end());
vector<AttSet> vInterSec;
set_intersection(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end(), vertices[vr]->attSet.begin(), vertices[vr]->attSet.end(), back_inserter(vInterSec));
*/}
You cannot have overloads that have the same signature. This holds for any function. How would you try to decide which version to use?
If you want sort the object based on different criteria you should use the sort version that takes a custom comparer function as the third argument.
Edit:
Of course you need to provide the comparer. I would suggest providing the comparers as static functions of the class if you have such power. This way you will not pollute enclosing namespace and you can access privates of the class with out exposing any getters. Since your properties are public the lambda would suffice, and probably be the best/cleanest approach.
Feeling adventurous I made a simple c++11 exercise program. For what it's worth, if you ever decided to go for proper encapsulation, I've shown both approaches:
#include <iostream>
#include <algorithm>
#include <vector>
#include <initializer_list>
#include <cassert>
using namespace std;
template<typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v){
for(const auto& el : v){
out << el << '\n';
}
return out;
}
class A {
int a;
int b;
public:
A(std::initializer_list<int> l){
assert(l.size() == 2);
auto i = l.begin();
a = *i;
++i;
b = *i;
}
friend std::ostream& operator<<(std::ostream& stream, const A& e){
return stream << e.a << ' ' << e.b;
}
static bool compareViaA(const A& lhs, const A& rhs){
return rhs.a > lhs.a;
}
static bool compareViaB(const A& lhs, const A& rhs){
return rhs.b > lhs.b;
}
};
int main() {
std::vector<A> v {{2,3}, {3,2}, {1,4}, {4,1}};
//sort(v.begin(), v.end(), [](const A& a, const A& b){return a.a > b.a;}) // fails because of privacy violation
sort(v.begin(), v.end(), A::compareViaA);
std::cout << v << '\n';
sort(v.begin(), v.end(), A::compareViaB);
std::cout << v << '\n';
return 0;
}
Live: http://ideone.com/lDMujx.
I think you can implement this by using functor and take the comparator(operator< overload) outside the AttSet.
Here is a simple example:
struct AtrComparator {
bool distcmp;
AttrComparator(bool distcmp): distcmp(distcmp) {}
bool operator() (const AttSet &s1, const AttSet &s2) {
if(distcmp) {
return s1.dist < s2.dist;
} else {
return s1.nodeID < s2.nodeID;
}
}
}
And then you can do the sort through different feed, dist or nodeID.
.e.g:
sort(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end(), AttComparator(true));
sort(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end(), AttComparator(false));
You can't do that. They have the same signature exactly.
Use a functor or a lambda and pass it to whatever algorithm you want.
std::sort(std::begin(container), std::end(container),
[](const element_type& lhs, const element_type& rhs) { return ...; });
Another way to do this:
struct compare_by_node_id {
bool operator()(const AttSet& lhs, const AttSet& rhs) const {
return lhs.nodeID < rhs.nodeID;
}
};
struct compare_by_dist {
bool operator()(const AttSet& lhs, const AttSet& rhs) const {
return lhs.dist < rhs.dist;
}
};
And you could pass that to the algorithm like:
std::sort(std::begin(container), std::end(container), compare_by_node_id());
you cannot do that because compiler doesn't see difference between:
bool operator < (const AttSet & str) const; //this const doesn't allow to override any property of object(instance of AttSet) if I remember
and
bool operator < (const AttSet & str);
there're the same same return type, same parameter (same signature)
compiler cannot choose which one is better
There's not a great way to do this as far as I am aware, since the compiler will see these as the exact same and will throw an error. If you need to do this, use the < operator as whatever will occur the most often, and then write a method that you can call to compare two object. Something like this:
bool operator< (const Blah &blah) const {
return (most often operation)
}
bool Blah::other_operation(const Blah &blah) const {
return (other operation)
}
I've read through the map::map reference at cplusplus.com and I'm still not sure how to get this to work. All I want to do is create a map like the following:
std::map<TriSpec, unsigned int> TriSpecMap;
Then I want to insert into it as follows:
result = TriSpecMap.insert(std::make_pair(triSpecObject, anUnsignedInt));
The following is a short example of my TriSpec header and .cpp:
//TriSpec.h
#ifndef TRISPEC_H
#define TRISPEC_H
class TriSpec
{
public:
TriSpec(void);
~TriSpec(void);
unsigned int m_position;
};
bool operator< (const TriSpec& lhs, const TriSpec& rhs);
#endif
//TriSpec.cpp
#include "TriSpec.h"
TriSpec::TriSpec(void){}
TriSpec::~TriSpec(void){}
bool operator< (const TriSpec& lhs, const TriSpec& rhs)
{
if (lhs.m_position < rhs.m_position) return true;
else return false;
}
Am I overloading the correct operator? Is the function/formatting I'm using correct? When I look at result.second, it is always true, even when I know the object being inserted should already exist in the map.
I do not see any problem with your code except that bool operator should be friend method (it does not even compile without friend keyword):
bool friend operator< (const TriSpec& lhs, const TriSpec& rhs)
{
return (lhs.m_position < rhs.m_position);
}
Then it works as expected:
int main(int argc, _TCHAR* argv[])
{
std::map<TriSpec, unsigned int> TriSpecMap;
TriSpec triSpecObject1;
triSpecObject1.m_position = 1;
TriSpec triSpecObject2;
triSpecObject2.m_position = 1;
TriSpec triSpecObject3;
triSpecObject3.m_position = 3;
std::pair<std::map<TriSpec, unsigned int>::iterator, bool> retVal =
TriSpecMap.insert(std::make_pair(triSpecObject1, 1));
retVal = TriSpecMap.insert(std::make_pair(triSpecObject2, 1));
retVal = TriSpecMap.insert(std::make_pair(triSpecObject3, 1));
return 0;
}
The result of first insertion is true, the result of second is false a the third is true again - as it should be. The map container contains two objects then - triSpecObject1 a triSpecObject3.
That doesn't look like it would compile - your operator< doesn't return a value in all cases. (Edit: you've fixed this, thanks.) You can simplify it quite a bit:
bool operator< (const TriSpec& lhs, const TriSpec& rhs)
{
return (lhs.m_position < rhs.m_position);
}
Since you don't show the complete code that inserts into the map, it's impossible to say why the return value .second always returns true.
Since C++11 you can also use a lambda expression instead of providing an operator< for your class. As a result, you can create your map with only two lines of code as follows:
int main()
{
auto comp = [](const TriSpec& t1, const TriSpec& t2) { return t1.m_position < t2.m_position; };
std::map<TriSpec, unsigned int, decltype(comp)> TriSpecMap(comp);
TriSpec t1, t2, t3;
t1.m_position = 1;
t2.m_position = 3;
t3.m_position = 5;
auto retVal = TriSpecMap.emplace(t1, 4);
retVal = TriSpecMap.emplace(t2, 2);
retVal = TriSpecMap.emplace(t3, 6);
for (auto const &kv : TriSpecMap)
std::cout << kv.first.m_position << ": " << kv.second << std::endl;
return 0
}
Output:
1: 4
3: 2
5: 6
Code on Ideone