Using custom std::set comparator
1. Modern C++20 solutionauto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s;
auto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s;
We use lambda function as comparator. As usual, comparator should return boolean value, indicating whether the element passed as first argument is considered to go before the second in the specific strict weak ordering it defines.
Online demo
2. Modern C++11 solutionauto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s(cmp);
auto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s(cmp);
Before C++20 we need to pass lambda as argument to set constructor
Online demo
3. Similar to first solution, but with function instead of lambda
Make comparator as usual boolean function
bool cmp(int a, int b) {
return ...;
}
Then use it, either this way:
std::set<int, decltype(cmp)*> s(cmp);
Online demo
or this way:
std::set<int, decltype(&cmp)> s(&cmp);
Online demo
4. Old solution using struct with ()
operatorstruct cmp {
bool operator() (int a, int b) const {
return ...
}
};
// ...
// later
std::set<int, cmp> s;
struct cmp {
bool operator() (int a, int b) const {
return ...
}
};
// ...
// later
std::set<int, cmp> s;
Online demo
5. Alternative solution: create struct from boolean function
Take boolean function
bool cmp(int a, int b) {
return ...;
}
And make struct from it using std::integral_constant
#include <type_traits>
using Cmp = std::integral_constant<decltype(&cmp), &cmp>;
Finally, use the struct as comparator
std::set<X, Cmp> set;
Online demo
std::set operations with custom comparator
Try changing
struct comp {
bool operator()(int const & a, int const & b) const {
return sz(a) >= sz(b);
}
};
with
struct comp {
bool operator()(int const & a, int const & b) const {
return sz(a) > sz(b);
} // ---------^
};
The (first) problem is that a comparator must impose a strict weak ordering.
So, in particular, must be comp(a, a) == false
for every a
in the std::set
.
With your comparator you have comp(a, a) == true
for every a
.
Anyway: this works only if a != b
imply s(a) != s(b)
; if this isn't the case... well... I suppose you can try with
struct comp {
bool operator()(int const & a, int const & b) const {
return (sz(a) > sz(b)) || ((sz(a) == sz(b)) && (a > b));
}
};
or something similar.
Comparator for member variable of type std::set that requires access to other member variables
CenterComparator
isn't related to ShapeDisplay
, it isn't aware of its members and it isn't derived from ShapeDisplay
. You need to provide CenterComparator
with its own reference Point
. You then need to provide an instance of CenterComparator
whose reference point is set.
Note that if you change that comparator's reference point in any way you will break std::set
's sorting resulting in Undefined Behavior if you try to use it. So whenever setReference
is called, you need to create a new set with a new comparator and copy over the old set.
Here is your code, adapted with these changes. I assumed you meant setReference
and insertShape
to be part of the public interface.
#include <cmath>
#include <set>
struct Point
{
int x;
int y;
};
struct Rectangle
{
int x;
int y;
int width;
int height;
};
class ShapeDisplay
{
public:
void insertShape(Rectangle rect)
{
m_shapes.insert(rect);
}
void setReference(Point reference)
{
m_reference = reference;
// Create a comparator using this new reference
auto comparator = CenterComparator{};
comparator.reference = m_reference;
// Create a new set
auto new_shapes = std::set<Rectangle, CenterComparator>(
std::begin(m_shapes), std::end(m_shapes), // Copy these shapes
comparator); // Use this comparator
m_shapes = std::move(new_shapes);
}
private:
struct CenterComparator
{
bool operator() (const Rectangle & a, const Rectangle & b) const
{
double distA = std::sqrt(std::pow(a.x - reference.x, 2)
+ std::pow(a.y - reference.y, 2));
double distB = std::sqrt(std::pow(b.x - reference.x, 2)
+ std::pow(b.y - reference.y, 2));
return distA < distB;
}
Point reference;
};
std::set<Rectangle, CenterComparator> m_shapes;
Point m_reference;
};
How to create a std::set with custom comparator in C++?
Method 1: use functor
Write a class that overloads the operator()
so it can be called like a function:
struct compare {
bool operator() (const pair<int,int> &lhs, const pair<int,int> &rhs) const{
return (lhs.second-lhs.first > rhs.second-rhs.first);
}
};
Then, you can use the class name as the type parameter
set<pair<int,int>, compare> myset;
Method 2: use function pointer
Assuming compare
is the function you want to use:
set<pair<int,int>, bool(*)(const pair<int,int> &lhs,
const pair<int,int> &rhs)
> myset(&compare);
C++ set with customized comparator crashes on insert
You have to pass pointer to compare function to set
constructor, otherwise it is null and it is why the code fails.
std::set<std::string, decltype(&Foo::Compare)> S{&Foo::Compare};
By decltype(&Foo::Compare)
you only specify the type of comparator, it has to be provided because its default value is null.
Using custom comparator for std::set with custom constructor
Making the set
You need to provide the comparer when you construct the set:
using std::unordered_map;
using std::set;
using std::string;
set<int, Custom> s(Custom(10, 20));
// Or:
auto s = set<int, Custom>(Custom(10, 20));
This is because it needs to have a comparer as soon as you start assigning elements to the set, and because your comparer has parameters, it needs to know what those are
Using the set in a map
The comparer has to be default-constructible because calling map["key"]
will default-construct the element if it doesn't exist:
class Custom
{
public:
// Add a default constructor
Custom() : x(0), y(0) {}
Custom (int x, int y) : x (x), y (x) {}
bool operator() (int a, int b)
{
return a < x && y < b;
}
private:
int x, y;
};
In this case, it's OK to provide a default constructor to the comparer because we can reassign it:
unordered_map<string, set<int, Custom>> map;
map["some key"] = set<int, Custom>(Custom(10, 20));
What if I don't or can't have a default constructor?
We can still use unordered_map
, but we have to use map.at("key")
and map.emplace("key", value)
instead of map["key"]
:
unordered_map<string, set<int, Custom>> map;
set<int, Custom> s(Custom(10, 20));
set<int, Custom> s2(Cunstom(30, 40));
s2.insert(1);
s2.insert(2);
s2.insert(3); // put some stuff in the set
map.emplace("Hello", s); //Inserts the set
map.insert({"Hello", s2}); //Inserts the set as a key-value pair
We can get values using map.at
:
// This line gets the set at "Hello", and inserts 10 into the set:
map.at("Hello").insert(10);
// If "Hello" isn't in the map, there's an error
And we can check if something's in the map by using map.find("key")
:
// Get an iterator to the key value pair held by "Hello" in the map:
// An iterator acts like a pointer
auto iterator = map.find("Hello");
if(iterator == map.end()) // Check if we found the thing
{
std::cout << "Couldn't find 'Hello' in the map";
}
else
{
// Get the key from the iterator
string key = iterator->first;
// Get a reference to the value from the iterator
set<int, Custom>& value = iterator->second;
}
C++ Finding minimum in vector of structs using custom comparator
It is evident that you need to define the comparison function correctly.
Here you are.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
struct element_t {
int val;
bool visited;
};
std::vector<element_t> vct_priority =
{
{ 1, true }, { 0, true }, { 1, false }, { 0, true },
{ 2, false }, { 2147483647, false }, { 2147483647, false },
{ 0, true }, {1, false }, { 0, true }, { 2, false },
{ 2147483647, false }, { 1, false }
};
auto less_not_visited = []( const auto &e1, const auto &e2 )
{
return ( not e1.visited ) && ( e2.visited or e1.val < e2.val );
};
auto min = std::min_element( std::begin( vct_priority ),
std::end( vct_priority ),
less_not_visited );
std::cout << std::distance( std::begin( vct_priority ), min ) << '\n';
}
The program output is
2
If you want to define a separate function instead of the lambda expression then it looks like
bool less_not_visited( const element_t &e1, const element_t &e2 )
{
return ( not e1.visited ) && ( e2.visited or e1.val < e2.val );
};
Related Topics
Significance of -Pthread Flag When Compiling
Converting a Hex String to a Byte Array
What Does 'Unsigned Temp:3' in a Struct or Union Mean
Is There a Difference Between Foo(Void) and Foo() in C++ or C
In C++ Books, Array Bound Must Be Constant Expression, But Why the Following Code Works
Splitting Templated C++ Classes into .Hpp/.Cpp Files - Is It Possible
Why Does C++ Disallow Anonymous Structs
Does the C++ Standard Allow For an Uninitialized Bool to Crash a Program
Pointer Expressions: *Ptr++, *++Ptr and ++*Ptr
How to Enable C++17 Compiling in Visual Studio
Operator Precedence VS Order of Evaluation
How to Link to a Library With Code::Blocks
Why Doesn't a Simple "Hello World"-Style Program Compile With Turbo C++
Is Uninitialized Local Variable the Fastest Random Number Generator
Why Should One Not Derive from C++ Std String Class
How to Correctly Implement Custom Iterators and Const_Iterators