Creating a set of objects [duplicate] - c++

This question already has answers here:
How can I use std::maps with user-defined types as key?
(8 answers)
Closed 9 years ago.
I am trying to create a set of objects. Merely defining the set is giving errors. I do not have a compiler right now with boost. So I used an online IDE. Here is the link to the code http://codepad.org/UsBAMmuh. Somehow, it does not seem to work. Eventually, I would like to pass a reference to this set in the constructor of another class.
#include <iostream>
#include <boost/optional.hpp>
#include <boost/ref.hpp>
#include <boost/serialization/vector.hpp>
using namespace std;
class Fruit {
};
class Env
{
public:
Env(std::set<Fruit>& apples);
std::set<Fruit>& GetApples() const;
void AddApple(Fruit const& fruit);
private:
std::set<Fruit>& _apples;
};
Env::Env(std::set<Fruit>& apples):
_apples(apples)
{
}
std::set<Fruit>& Env::GetApples() const
{
return _apples;
}
void Env::AddApple(Fruit const& fruit)
{
this->_apples.insert(fruit);
}
class EnvHolder{
public:
void SetEnv (Env const& env);
Env& GetEnv()const;
private:
boost::scoped_ptr<Env> _env;
};
void EnvHolder::SetEnv(Env const& env)
{
this->_env.reset(new Env(env));
}
Env& EnvHolder::GetEnv() const
{
return *this->_env;
}
int main() {
std::set<Fruit> fruits;
//Fruit *fr = new Fruit();
//fruits.insert(*fr);
//Env env(fruits);
cout << "Hello" << endl;
return 0;
}
I get the following error:
/usr/local/lib/gcc/i686-pc-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_function.h: In member function 'bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Fruit]':
/usr/local/lib/gcc/i686-pc-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/boost_concept_check.h:358: instantiated from 'void __gnu_cxx::_BinaryFunctionConcept<_Func, _Return, _First, Second>::_constraints() [with _Func = std::less, _Return = bool, _First = Fruit, _Second = Fruit]'
/usr/local/lib/gcc/i686-pc-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_set.h:112: instantiated from '__gnu_norm::set, std::allocator >'
/usr/local/lib/gcc/i686-pc-linux-gnu/4.1.2/../../../../include/c++/4.1.2/debug/set.h:45: instantiated from '__gnu_debug_def::set, std::allocator >'
t.cpp:35: instantiated from here
Line 226: error: no match for 'operator<' in '__x < __y'
compilation terminated due to -Wfatal-errors.

When you want to put objects of a class into an associative container, e.g., a std::set<T>, you'll need to provide some way to identify the objects in that container. The ordered associative containers (std::map<K, V>, std::set<T> and their "multi"-versions) use a strict weak ordering to to identify object. This means you either define a suitable less than operator or you provide a comparison type when defining the std::set<T>. For starters it is probably easiest to define a less than operator, e.g.:
class Fruit {
std::string name_;
public:
Fruit(std::string const& name): name_(name) {}
std::string name() const { return this->name_; }
};
bool operator< (Fruit const& f0, Fruit const& f1) {
return f0.name() < f1.name();
}
bool operator> (Fruit const& f0, Fruit const& f1) { return (f1 < f0); }
bool operator<= (Fruit const& f0, Fruit const& f1) { return !(f1 < f0); }
bool operator>= (Fruit const& f0, Fruit const& f1) { return !(f0 < f1); }
Although adding these operators will make your code compile, it probably won't make it work too well: since the Fruit class has no members, there is no way to distinguish them. Hence, all Fruit objects are considered to be the in the same equivalence class. For an actual Fruit class you would have some members and you would order your objects according to them.
Although strictly speaking only operator<() is needed, it is reasonable to provide the other relation operators, too. They always look the same, though, i.e., they simply delegate to operator<(). Aside from choosing to implement full set of relation operators, the code also chooses to implement the comparison operators as non-member functions: the operators could also be implemented as member function. However, if there are implicit conversion involved, the member operators behave asymmetric: the allow conversions on the right hand side operand but not on the left hand side operand. The non-member implementation make the conversion behavior symmetrical.
Using a comparison type would look something like this:
struct FruitCmp {
bool operator()(Fruit const& f0, Fruit const& f1) const {
return f0.name() < f1.name();
}
};
std::set<Fruit, FruitCmp> setOfFruit;
Of course, in all these case where false is return the proper logic implementing a strict weak ordering need to go. Typically, the easiest approach is to define the comparison in terms of a lexicographical comparison of the key members.

Related

Using std::unique_ptr of a polymorphic class as key in std::unordered_map

My problem comes from a project that I'm supposed to finish. I have to create an std::unordered_map<T, unsigned int> where T is a pointer to a base, polymorphic class. After a while, I figured that it will also be a good practice to use an std::unique_ptr<T> as a key, since my map is meant to own the objects. Let me introduce some backstory:
Consider class hierarchy with polymorphic sell_obj as a base class. book and table inheriting from that class. We now know that we need to create a std::unordered_map<std::unique_ptr<sell_obj*>, unsigned int>. Therefore, erasing a pair from that map will automatically free the memory pointed by key. The whole idea is to have keys pointing to books/tables and value of those keys will represent the amount of that product that our shop contains.
As we are dealing with std::unordered_map, we should specify hashes for all three classes. To simplify things, I specified them in main like this:
namespace std{
template <> struct hash<book>{
size_t operator()(const book& b) const
{
return 1; // simplified
}
};
template <> struct hash<table>{
size_t operator()(const table& b) const
{
return 2; // simplified
}
};
// The standard provides a specilization so that std::hash<unique_ptr<T>> is the same as std::hash<T*>.
template <> struct hash<sell_obj*>{
size_t operator()(const sell_obj *s) const
{
const book *b_p = dynamic_cast<const book*>(s);
if(b_p != nullptr) return std::hash<book>()(*b_p);
else{
const table *t_p = static_cast<const table*>(s);
return std::hash<table>()(*t_p);
}
}
};
}
Now let's look at implementation of the map. We have a class called Shop which looks like this:
#include "sell_obj.h"
#include "book.h"
#include "table.h"
#include <unordered_map>
#include <memory>
class Shop
{
public:
Shop();
void add_sell_obj(sell_obj&);
void remove_sell_obj(sell_obj&);
private:
std::unordered_map<std::unique_ptr<sell_obj>, unsigned int> storeroom;
};
and implementation of two, crucial functions:
void Shop::add_sell_obj(sell_obj& s_o)
{
std::unique_ptr<sell_obj> n_ptr(&s_o);
storeroom[std::move(n_ptr)]++;
}
void Shop::remove_sell_obj(sell_obj& s_o)
{
std::unique_ptr<sell_obj> n_ptr(&s_o);
auto target = storeroom.find(std::move(n_ptr));
if(target != storeroom.end() && target->second > 0) target->second--;
}
in my main I try to run the following code:
int main()
{
book *b1 = new book("foo", "bar", 10);
sell_obj *ptr = b1;
Shop S_H;
S_H.add_sell_obj(*ptr); // works fine I guess
S_H.remove_sell_obj(*ptr); // usually (not always) crashes [SIGSEGV]
return 0;
}
my question is - where does my logic fail? I heard that it's fine to use std::unique_ptr in STL containters since C++11. What's causing the crash? Debugger does not provide any information besides the crash occurance.
If more information about the project will be needed, please point it out. Thank you for reading
There are quite a few problems with logic in the question. First of all:
Consider class hierarchy with polymorphic sell_obj as base class. book and table inheriting from that class. We now know that we need to create a std::unordered_map<std::unique_ptr<sell_obj*>, unsigned int>.
In such cases std::unique_ptr<sell_obj*> is not what we would want. We would want std::unique_ptr<sell_obj>. Without the *. std::unique_ptr is already "a pointer".
As we are dealing with std::unordered_map, we should specify hashes for all three classes. To simplify things, I specified them in main like this: [...]
This is also quite of an undesired approach. This would require changing that part of the code every time we add another subclass in the hierarchy. It would be best to delegate the hashing (and comparing) polymorphically to avoid such problems, exactly as #1201programalarm suggested.
[...] implementation of two, crucial functions:
void Shop::add_sell_obj(sell_obj& s_o)
{
std::unique_ptr<sell_obj> n_ptr(&s_o);
storeroom[std::move(n_ptr)]++;
}
void Shop::remove_sell_obj(sell_obj& s_o)
{
std::unique_ptr<sell_obj> n_ptr(&s_o);
auto target = storeroom.find(std::move(n_ptr));
if(target != storeroom.end() && target->second > 0) target->second--;
}
This is wrong for couple of reasons. First of all, taking an argument by non-const reference suggest modification of the object. Second of all, the creation of n_ptr from a pointer obtained by using & on an argumnet is incredibly risky. It assumes that the object is allocated on the heap and it is unowned. A situation that generally should not take place and is incredibly dangerous. In case where the passed object is on the stack and / or is already managed by some other owner, this is a recipe for a disaster (like a segfault).
What's more, it is more or less guaranteed to end up in a disaster, since both add_sell_obj() and remove_sell_obj() create std::unique_ptrs to potentially the same object. This is exactly the case from the original question's main(). Two std::unique_ptrs pointing to the same object result in double delete.
While it's not necessarily the best approach for this problem if one uses C++ (as compared to Java), there are couple of interesting tools that can be used for this task. The code below assumes C++20.
The class hierarchy
First of all, we need a base class that will be used when referring to all the objects stored in the shop:
struct sell_object { };
And then we need to introduce classes that will represent conrete objects:
class book : public sell_object {
std::string title;
public:
book(std::string title) : title(std::move(title)) { }
};
class table : public sell_object {
int number_of_legs = 0;
public:
table(int number_of_legs) : number_of_legs(number_of_legs) { }
};
For simplicity (but to still have some distinctions) I chose for them to have just one, distinct field (title and number_of_legs).
The storage
The shop class that will represent storage for any sell_object needs to somehow store, well, any sell_object. For that we either need to use pointers or references to the base class. You can't have a container of references, so it's best to use pointers. Smart pointers.
Originally the question suggested the usage of std::unordered_map. Let us stick with it:
class shop {
std::unordered_map<
std::unique_ptr<sell_object>, int,
> storage;
public:
auto add(...) -> void {
...
}
auto remove(...) -> void {
...
}
};
It is worth mentioning that we chose std::unique_ptr as key for our map. That means that the storage is going to copy the passed objects and use the copies it owns to compare with elements we query (add or remove). No more than one equal object will be copied, though.
The fixed version of storage
There is a problem, however. std::unordered_map uses hashing and we need to provide a hash strategy for std::unique_ptr<sell_object>. Well, there already is one and it uses the hash strategy for T*. The problem is that we want to have custom hashing. Those particular std::unique_ptr<sell_object>s should be hashed according to the associated sell_objects.
Because of this, I opt to choose a different approach than the one proposed in the question. Instead of providing a global specialization in the std namespace, I will choose a custom hashing object and a custom comparator:
class shop {
struct sell_object_hash {
auto operator()(std::unique_ptr<sell_object> const& object) const -> std::size_t {
return object->hash();
}
};
struct sell_object_equal {
auto operator()(
std::unique_ptr<sell_object> const& lhs,
std::unique_ptr<sell_object> const& rhs
) const -> bool {
return (*lhs <=> *rhs) == 0;
}
};
std::unordered_map<
std::unique_ptr<sell_object>, int,
sell_object_hash, sell_object_equal
> storage;
public:
auto add(...) -> void {
...
}
auto remove(...) -> void {
...
}
};
Notice a few things. First of all, the type of storage has changed. No longer it is an std::unordered_map<std::unique_ptr<T>, int>, but an std::unordered_map<std::unique_ptr<T>, int, sell_object_hash, sell_object_equal>. This is to indicate that we are using custom hasher (sell_object_hash) and custom comparator (sell_object_equal).
The lines we need to pay extra attention are:
return object->hash();
return (*lhs <=> *rhs) == 0;
Onto them:
return object->hash();
This is a delegation of hashing. Instead of being an observer and trying to have a type that for each and every possible type derived from sell_object implements a different hashing, we require that those objects supply the sufficient hashing themselves. In the original question, the std::hash specialization was the said "observer". It certainly did not scale as a solution.
In order to achieve the aforementioned, we modify the base class to impose the listed requirement:
struct sell_object {
virtual auto hash() const -> std::size_t = 0;
};
Thus we also need to change our book and table classes:
class book : public sell_object {
std::string title;
public:
book(std::string title) : title(std::move(title)) { }
auto hash() const -> std::size_t override {
return std::hash<std::string>()(title);
}
};
class table : public sell_object {
int number_of_legs = 0;
public:
table(int number_of_legs) : number_of_legs(number_of_legs) { }
auto hash() const -> std::size_t override {
return std::hash<int>()(number_of_legs);
}
};
return (*lhs <=> *rhs) == 0;
This is a C++20 feature called the three-way comparison operator, sometimes called the spaceship operator. I opted into using it, since starting with C++20, most types that desire to be comparable will be using this operator. That means we also need our concrete classes to implement it. What's more, we need to be able to call it with base references (sell_object&). Yet another virtual function (operator, actually) needs to be added to the base class:
struct sell_object {
virtual auto hash() const -> std::size_t = 0;
virtual auto operator<=>(sell_object const&) const -> std::partial_ordering = 0;
};
Every subclass of sell_object is going to be required to be comparable with other sell_objects. The main reason is that we need to compare sell_objects in our storage map. For completeness, I used std::partial_ordering, since we require every sell_object to be comparable with every other sell_object. While comparing two books or two tables yields strong ordering (total ordering where two equivalent objects are indistinguishable), we also - by design - need to support comparing a book to a table. This is somewhat meaningless (always returns false). Fortunately, C++20 helps us here with std::partial_ordering::unordered. Those elements are not equal and neither of them is greater or less than the other. Perfect for such scenarios.
Our concrete classes need to change accordingly:
class book : public sell_object {
std::string title;
public:
book(std::string title) : title(std::move(title)) { }
auto hash() const -> std::size_t override {
return std::hash<std::string>()(title);
}
auto operator<=>(book const& other) const {
return title <=> other.title;
};
auto operator<=>(sell_object const& other) const -> std::partial_ordering override {
if (auto book_ptr = dynamic_cast<book const*>(&other)) {
return *this <=> *book_ptr;
} else {
return std::partial_ordering::unordered;
}
}
};
class table : public sell_object {
int number_of_legs = 0;
public:
table(int number_of_legs) : number_of_legs(number_of_legs) { }
auto hash() const -> std::size_t override {
return std::hash<int>()(number_of_legs);
}
auto operator<=>(table const& other) const {
return number_of_legs <=> other.number_of_legs;
};
auto operator<=>(sell_object const& other) const -> std::partial_ordering override {
if (auto table_ptr = dynamic_cast<table const*>(&other)) {
return *this <=> *table_ptr;
} else {
return std::partial_ordering::unordered;
}
}
};
The overriden operator<=>s are required due to the base class' requirements. They are quite simple - if the other object (the one we are comparing this object to) is of the same type, we delegate to the <=> version that uses the concrete type. If not, we have a type mismatch and we report the unordered ordering.
For those of you who are curious why the <=> implementation that compares two, identical types is not = defaulted: it would use the base-class comparison first, which would delegate to the sell_object version. That would dynamic_cast again and delegate to the defaulted implementation. Which would compare the base class and... result in an infinite recursion.
add() and remove() implementation
Everything seems great, so we can move on to adding and removing items to and from our shop. However, we immediately arrive at a hard design decision. What arguments should add() and remove() accept?
std::unique_ptr<sell_object>? That would make their implementation trivial, but it would require the user to construct a potentially useless, dynamically allocated object just to call a function.
sell_object const&? That seems correct, but there are two problems with it: 1) we would still need to construct an std::unique_ptr with a copy of passed argument to find the appropriate element to remove; 2) we wouldn't be able to correctly implement add(), since we need the concrete type to construct an actual std::unique_ptr to put into our map.
Let us go with the second option and fix the first problem. We certainly do not want to construct a useless and expensive object just to look for it in the storage map. Ideally we would like to find a key (std::unique_ptr<sell_object>) that matches the passed object. Fortunately, transparent hashers and comparators come to the rescue.
By supplying additional overloads for hasher and comparator (and providing a public is_transparent alias), we allow for looking for a key that is equivalent, without needing the types to match:
struct sell_object_hash {
auto operator()(std::unique_ptr<sell_object> const& object) const -> std::size_t {
return object->hash();
}
auto operator()(sell_object const& object) const -> std::size_t {
return object.hash();
}
using is_transparent = void;
};
struct sell_object_equal {
auto operator()(
std::unique_ptr<sell_object> const& lhs,
std::unique_ptr<sell_object> const& rhs
) const -> bool {
return (*lhs <=> *rhs) == 0;
}
auto operator()(
sell_object const& lhs,
std::unique_ptr<sell_object> const& rhs
) const -> bool {
return (lhs <=> *rhs) == 0;
}
auto operator()(
std::unique_ptr<sell_object> const& lhs,
sell_object const& rhs
) const -> bool {
return (*lhs <=> rhs) == 0;
}
using is_transparent = void;
};
Thanks to that, we can now implement shop::remove() like so:
auto remove(sell_object const& to_remove) -> void {
if (auto it = storage.find(to_remove); it != storage.end()) {
it->second--;
if (it->second == 0) {
storage.erase(it);
}
}
}
Since our comparator and hasher are transparent, we can find() an element that is equivalent to the argument. If we find it, we decrement the corresponding count. If it reaches 0, we remove the entry completely.
Great, onto the second problem. Let us list the requirements for the shop::add():
we need the concrete type of the object (merely a reference to the base class is not enough, since we need to create matching std::unique_ptr).
we need that type to be derived from sell_object.
We can achieve both with a constrained* template:
template <std::derived_from<sell_object> T>
auto add(T const& to_add) -> void {
if (auto it = storage.find(to_add); it != storage.end()) {
it->second++;
} else {
storage[std::make_unique<T>(to_add)] = 1;
}
}
This is, again, quite simple
*References: {1} {2}
Correct destruction semantics
There is only one more thing that separates us from the correct implementation. It's the fact that if we have a pointer (either smart or not) to a base class that is used to deallocate it, the destructor needs to be virtual.
This leads us to the final version of the sell_object class:
struct sell_object {
virtual auto hash() const -> std::size_t = 0;
virtual auto operator<=>(sell_object const&) const -> std::partial_ordering = 0;
virtual ~sell_object() = default;
};
See full implementation with example and additional printing utilities.

C++ strict weak ordering derived class

I'm trying to implement strict weak ordering in a subclass that I want to place in an STL set container. The STL set uses operator< to order its elements in strict weak ordering. In my case I have a hierarchy of types and I am not quite sure how to achieve this for derived types. To this end, I put together a quick live demo showing where I am uncertain. I am ordering the fields using an easy to use std::tie technique. My area of uncertainty is how I should call the superclass' operator< prior to calling the std::tie comparison on the derived fields.
struct Base {
Base(const int& rIntVal, const std::string& rStrVal)
: mIntVal(rIntVal)
, mStrVal(rStrVal)
{}
inline bool operator<(const Base& rhs) const {
return std::tie(mIntVal, mStrVal) < std::tie(rhs.mIntVal, rhs.mStrVal);
}
private:
int mIntVal;
std::string mStrVal;
};
struct Derived : public Base {
Derived(
const int& rIntVal,
const std::string& rStrVal,
const std::string& rOtherStrVal,
const std::string& rOtherStrVal1)
: Base(rIntVal, rStrVal)
, mOtherStrVal(rOtherStrVal)
, mOtherStrVal1(rOtherStrVal1)
{}
inline bool operator<(const Derived& rhs) const {
// not sure what to do here - this is my best guess???
if( Base::operator<(rhs) ) {
return std::tie(mOtherStrVal, mOtherStrVal1) <
std::tie(rhs.mOtherStrVal, rhs.mOtherStrVal1);
} else {
return false;
}
}
private:
std::string mOtherStrVal;
std::string mOtherStrVal1;
};
You would do best to tie a reference to the base class:
bool operator<(const Derived& rhs) const {
return std::tie(static_cast<const Base&>(*this), mOtherStrVal, mOtherStrVal1) <
std::tie(static_cast<const Base&>(rhs), rhs.mOtherStrVal, rhs.mOtherStrVal1);
}
This will compare by the superclass fields first and then by the subclass fields.
Firstly, you could choose to have the derived fields take priority over the base ones, so that the derived members are considered first, or you could give the base fields priority. Which to do depends on the meaning of your class and how it should be sorted.
You have chosen to compare the base fields first, which is fine, so we'll go with that.
To be a strict weak ordering you should only compare the derived fields when the base sub-objects are equivalent (i.e. neither is less-than the other).
With your current code if you have lhs.mIntVal < rhs.mIntVal you should return true, but instead you go on to compare the derived fields, and might end up saying that lhs is not less than rhs even though the the result from the base class says it is.
So to make the result correct you need something equivalent to:
bool operator<(const Derived& rhs) const {
if (Base::operator<(rhs))
return true;
else if (rhs.Base::operator<(*this))
return false;
// base parts are equivalent, compare derived parts:
return std::tie(mOtherStrVal, mOtherStrVal1) <
std::tie(rhs.mOtherStrVal, rhs.mOtherStrVal1);
}
This is logically correct, but sub-optimal because you call Base::operator< twice. You could avoid that by including the base objects in the tie expression as ecatmur shows.

Comparison function for C++ STL set: Can comparison function be a member function of a class?

I have to use STL set and I want to define my own comparison function. But as per my requirement this comparison function shouldn't be a global, rather should be a public member of a class.
//CLASS DEFINITION
class IDENTIFERS
{
public:
IDENTIFIERS();
~IDENTIFIERS();
bool compare_identifier(int Identifier, int Identifier);
public:
std::set <Identifier, bool(*)(Identifier, Identifier)> set_instance;
};
//CLASS Constructor
IDENTIFIERS::IDENTIFIERS()
{
std::set <Identifier, bool(*)(Identifier, Identifier)> set_instance(compare_identifier);
}
If I write a piece a code as mentioned above. It doesn't compile as comparison function's prototype doesn't match with compare_identifier() function.
Is there a way to do it?
A non-static member function takes an implicit first parameter for this, so your compare_identifier actually has three parameters. If you need it to be a non-static member function, you need to bind the member function's implicit first parameter to an instance of IDENTIFIERS, for example,
#include <set>
#include <functional>
struct Identifier { int id; };
class IDENTIFERS
{
public:
IDENTIFERS() : set_instance(std::bind(&IDENTIFERS::compare_identifier,
this,
std::placeholders::_1,
std::placeholders::_2))
{}
bool compare_identifier(const Identifier& lhs, const Identifier& rhs)
{
return lhs.id < rhs.id;
}
public:
std::set <Identifier, std::function<bool(const Identifier&, const Identifier&)>> set_instance;
};
There are a number of errors in your sample code.
The zero-th error is somewhat style-related: classes should be named all-lowercase-with-underscores or CamelCase.
The first error:
bool compare_identifier(int Identifier, int Identifier);
you can't have the same identifier for both function parameters (also, style-related, function parameters should be all-lowercase or camelCased), like
bool compare_identifier(int id1, int id2);
The second error is that this:
std::set <Identifier, bool(*)(Identifier, Identifier)> set_instance;
assumes that you have a class Identifier somewhere. Which something tells me you don't, since your class is called IDENTIFIERS. If you do have an Identifier class, I should assume that your compare_identifier function is like:
bool compare_identifier(const Identifier& id1, const Identifier& id2);
and that your set_instance declaration is like:
std::set<Identifier, bool(*)(const Identifier&, const Identifier&)> set_instance;
The third error is that your constructor does not do what you are assuming it does (constructing the set_instance member), but it would be (if syntatically valid) constructing a local variable called set_instance. So, your constructor should go something like:
IdentifierBucket::IdentifierBucket() :
set_instance(std::bind(compare_identifier, this, _1, _2)) {
}
But all this is moot... because the comparator for set also does not do what you think it does (see if two items are equal or not) but it gives them order (the default comparator is std::less...
What you really want is something like:
#include <set>
#include <functional>
struct Identifier {};
struct IdentifierComparator {
IdentifierComparator() {};
bool operator()(Identifier id1, Identifier id2); // WRITE THIS
};
class IdentifierBucket {
public:
IdentifierBucket() {};
~IdentifierBucket() {};
private:
std::set <Identifier, IdentifierComparator> set_instance;
};
Making compare_identifier() static is the easiest way to solve this. Here is how you can fix your code so that it compiles, assuming Identifier is defined elsewhere:
// Watch for the typo (missing 'I') in your code above
class IDENTIFIERS // Note, all-uppercase names are usually reserved for macros
{
public:
IDENTIFIERS();
// N.B: This function needs to take two `Identifier`s as its arguments
// (not two `int`s)
static bool compare_identifier(Identifier const &l, Identifier const& r)
{
return l.custom_less_than(r);
}
// typedefs can make your code more readable, especially with function pointers.
typedef bool (*identifier_comparator)(Identifier const &l, Identifier const& r);
typedef std::set<Identifier, identifier_comparator> identifier_set;
identifier_set set_instance;
};
IDENTIFIERS::IDENTIFIERS()
:set_instance(compare_identifier) // Initialise here
{
// Don't shadow `set_instance` in the body of the constructor!
}
Following comment
Making compare_identifier() static (in IDENTIFIERS) will not affect the accessibility of members of the Identifier class. The logic to compare two Identifier objects should reside either within Identifier, like this:
class Identifier
{
public:
// If you define this, then you won't need to specify a custom comparator
bool operator <(Identifier const& r) const
{
// return true if `*this` is logically less-than `r`; false otherwise.
}
bool custom_less_than(Identifier const& r) const
{
// Some other way of comparing objects. Perhaps reverse-alphabetically
// or similar.
// return true if `*this` is logically less-than `r`; false otherwise.
}
};
...or within free functions but these won't have access to the private members.

how to use overloaded std::less for std::map

I have the following snippet:
typedef char OR[12];
class COR
{
OR m_or;
public:
COR(const char* or) { strcpy(m_or, or); }
COR(const COR& o) { strcpy(m_or, o.m_or); }
const char* GetOR() const { return m_or; }
#if 0 // I do not wish to use this as it will create a temporary object
bool operator<(const COR& left, const COR& right) const
{ return (strcmp(left.m_or, right.m_or) < 0); }
#endif
};
namespace std {
template<>
struct less<COR> {
bool operator()(const COR& cor, const char* or) const
{ return (strcmp(cor.GetOR(), or) < 0); }
};
}
When I try this I get an error:
error: no matching function for call to std::map<COR, SomeStruct*, std::less<COR>, std::allocator<std::pair<const COR, SomeStruct*> > >::find(const char*&)
I do not want to use any method that involves comparison of two "COR" objects. I will have comparison of COR with const char*. Can you guys suggest a way to do it?
Several methods:
Note: None of these generate extra copies as the values are always passed by reference (and since we don't normally mutate an object on comparison pass by const reference).
Method 1:
class COR
{
public:
// 1: Make it a member function
// Thus you only specify the right hand side.
// The left is implicit.
bool operator<(COR const& right) const
{
return (strcmp(m_or, right.m_or) < 0);
}
};
method 2:
class COR
{
public:
// 2: Make it a friend non member function
// Note: Just because I declare it here does not make it part of the class.
// This is a separate non member function
// The compiler makes the destinction because of the `friened`
friend bool operator<(COR const& left, COR const& right)
{
return (strcmp(left.m_or, right.m_or) < 0);
}
};
Method 3:
class COR
{
public:
// Just an example. Just need some way for the functor to access members
// In a way that will allow a strict weak ordering.
bool test(COR const& right) const {return (strcmp(m_or, right.m_or) < 0);}
};
// Define a functor.
// This is just a class with the operator() overloaded so that it can
// act like a function. You can make it do whatever you like but for
// comparisons it will be passed two members of the container (accept by const
// reference and make sure the functor is const member and things will go well).
struct CorTest
{
bool operator()(COR const& left, COR const& right) const
{
return left.test(right);
}
};
// When you declare the set you just pass as the second template parameter.
// (or third if it is a map)
std::set<COR, CorTest> mySet;
std::map<COR, int, CorTest> myMap;
The method you use will depend on situation.
In most situations I would use method (1). If there is a special sort order I needed for a one off event that I want to use with a sorted container then I would use method (3). method (2) can be used as an alternative to method (1) and in some situations is better (but you need to provide more details about usage before I would say use this).
You got this error because you made a mistake - the arguments type of the std::less operator() must be identical. So, this works:
template <>
struct std::less<COR>
{
bool operator()(const COR &a, const COR &b) const { return (strcmp(a.GetOR(), b.GetOR()) < 0); }
};
You're looking for the new overloaded versions of find(), lower_bound(), and upper_bound() added in C++14. They do exactly what you are asking for.
https://en.cppreference.com/w/cpp/container/set/find

How can I use a std::tr1::function object in a key to unordered_map?

I'm trying to form a std::tr1::unordered_map where the key type is a struct that includes a callback function, for which I'm using std::tr1::function. I'm running into two problems: 1) the function object does not appear to be equality comparable, as the Boost.Function documentation indicates; 2) I don't see how to implement the hash function, since I can't get a regular function pointer (or something else I could use for hashing) from the function object.
Here's the example code:
#include <boost/functional/hash.hpp>
#include <boost/tr1/functional.hpp>
#include <boost/tr1/unordered_map.hpp>
#include <iostream>
int f(int) {}
typedef std::tr1::function<int(int)> callback;
struct Record
{
callback func;
// More members...
// Requirements for unordered_map key.
friend bool operator==(Record const & lhs, Record const & rhs)
{ return lhs.func == rhs.func; } // error: ambiguous
friend std::size_t hash_value(Record const & arg)
{ return boost::hash<void *>(arg.func.get()); } // error: no member get()
};
int main()
{
std::tr1::unordered_map<Record, int> map;
Record a = {f};
map[a] = 0;
return 0;
}
Here's some detail on the first error :
test.cpp: In function bool operator==(const Record&, const Record&):
test.cpp:16: error: ambiguous overload for operator== in lhs->Record::func == rhs->Record::func
test.cpp:16: note: candidates are: operator==(void (boost::function1<int, int>::dummy::*)(), void (boost::function1<int, int>::dummy::*)()) <built-in>
<root>/boost/function/function_template.hpp:1024: note: void boost::operator==(const boost::function1<R, T0>&, const boost::function1<R, T0>&) [with R = int, T0 = int]
For the second error, obviously there's no function<...>::get member, but what should I use instead?
I'm using Boost version 1.42 and g++ 4.2.2. Thanks for any help.
Update
The answer to the posted question is "you can't." tr1::function objects are hashable (e.g., using boost::hash), but are not equality comparable. If you want to use a function in the hash key, rethink the approach or find a workaround.
It would seem that TR1 specifically requires that
template<class Function2> bool operator==(const function<Function2>&);
template<class Function2> bool operator!=(const function<Function2>&);
remain undefined (3.7.2.6), so at the very least you'd have to find another way to get equality. Furthermore, I'm not finding any reference to a get() member method in the paper either.
I can answer my own question as regards to the hash_value. This is the correct way to call boost::hash with a tr1::function:
friend std::size_t hash_value(Record const & arg)
{
boost::hash<callback> hasher;
return hasher(arg.func);
}
There are some ideas using function::target discussed here and here. You may also want to consider the Boost.Signals library as it's designed to support registration of callbacks.