This code works but having a struct called ptrcomp outside the weighted_pointer seems (to me) that they are different things. I tried some different ways and even googled it, but I haven't found anything that works like this.
struct node{
unsigned int oper;
void * a;
void * b;
};
struct weighted_pointer{
mutable int weight;
unique_ptr<node> pointer;
};
struct ptrcomp{
bool operator()(const weighted_pointer & lhs, const weighted_pointer & rhs) {
return tie(lhs.pointer->oper, lhs.pointer->a, lhs.pointer->b) < tie(rhs.pointer->oper, rhs.pointer->a, rhs.pointer->b);
}
};
set<weighted_pointer,ptrcomp> gate;
My objective is to make the std::set working. And possibly write it like set<weighted_pointer>.
having a struct called ptrcomp outside the weighted_pointer seems (to me) that they are different things.
That's how things really are. weighted_pointer is data, while ptrcomp is a way to compare the data. So, these two really are different things, and there is nothing wrong with your code.
If it happens that you have one canonical way of comparing your data, make it into operator <:
bool operator < (const weighted_pointer & lhs, const weighted_pointer & rhs) {
return tie(lhs.pointer->oper, lhs.pointer->a, lhs.pointer->b) < tie(rhs.pointer->oper, rhs.pointer->a, rhs.pointer->b);
}
std::set will happily use it, if you use it as std::set<weighted_pointer> (in fact, std::set has the second template parameter defaulted to std::less<T>, which is a comparator class that uses operator <).
If you change your code to
struct weighted_pointer {
mutable int weight;
unique_ptr<node> pointer;
bool operator < (const weighted_pointer & rhs) const;
};
bool weighted_pointer::operator < (const weighted_pointer & rhs) const {
return tie(pointer->oper, pointer->a, pointer->b) < tie(rhs.pointer->oper, rhs.pointer->a, rhs.pointer->b);
}
then it will work and you won't need a comparator ptrcomp for the set and can use the type set<weighted_pointer> as you wished. (You can also move the definition into the struct if you wish.)
struct weighted_pointer {
// ...
struct compare {
// ...
};
};
set<weighted_pointer,weighted_pointer::compare> gate;
// better
using weighted_pointer_set = set<weighted_pointer,weighted_pointer::compare>;
weighted_pointer_set gate;
This is how I see this usually done.
Having a std::set<weighted_pointer> means that the set uses std::less to compare the elements. This in turn calls operator< on the respective type, so if you provide an implementation of that operator it'll work.
Related
Let us say that p below has to be a pointer to const X. Then it is not possible to call find for a set of pointers to X with my special compare class. Is that a shortcoming of 'set' and 'find'? Is it safe to solve it with const_cast as I have done?
struct X{
std::string key;
X(std::string s): key(s) {}
};
struct compare {
bool operator() (const X* lhs, const X* rhs) const {
return lhs->key < rhs->key;
}
};
int main() {
std::set<X*,compare> m;
const X a("hello");
const X*p=&a;
std::set<X*,compare>::const_iterator it=m.find(const_cast<X*>(p));
}
This use of const_cast is safe, but any usage of const_cast is scary. const_cast is legal so long as you don't modify the object through the cast, which std::set::find does not do.
However, you don't need a const_cast here. If you make your comparator transparent, that opts into allowing find to search based on anything comparable to the key type. This is exactly what we want:
struct compare {
using is_transparent = void; // doesn't matter which type you use
bool operator() (const X* lhs, const X* rhs) const {
// You might want to consider using std::less<X*> to compare these.
// std::less<T*> can compare pointers which are not part of the
// same array, which is not allowed with just a simple less-than
// comparison.
return lhs->key < rhs->key;
}
};
Complete example: https://godbolt.org/z/NsZccs
I have the following struct
struct MyClass {
int myInt;
std::map<int, int> myMap;
};
I want to use unordered_set<MyClass*, PointedObjHash, PointedObEq> but I can't find a valid way to declare PointedObEq.
I tried
struct PointedObjHash {
size_t operator() (MyClass* const& c) const {
std::size_t seed = 0;
boost::hash_combine(seed, c->myInt);
boost::hash_combine(seed, c->myMap);
return seed;
}
and I hope it is fine, but I can't find a way to declare PointedObjEq
--- EDIT ---
If declare operator== inside the class debug never breaks, but I think 'cause MyClass == MyClass* never happens...
struct MyClass {
...
...
bool operator==(MyClass* const& c) {
return this->myInt == c->myInt & this->myMap == c->myMap;
}
If declare operator== inside the class debug never breaks, but I think 'cause MyClass == MyClass* never happens...
The unordered_set needs to use operator== (or PointedObjEq) to double-check the results of the hash function. The hash provides approximate equality, the equality function is used to weed out false positives.
If you've tested adding the same value to the set twice, then you've tested the equality function. To be sure, of course, you can have it print something to the console.
Since it's impossible to define an operator== function with two pointer operands, the PointedObjEq class will be necessary. Note that it takes a MyClass const * on both sides. Also, there's no need to use a reference to a pointer.
So,
struct PointedObjEq {
bool operator () ( MyClass const * lhs, MyClass const * rhs ) const {
return lhs->myInt == rhs->myInt
&& lhs->myMap == rhs->myMap;
}
};
This should do:
struct PointedObEq {
bool operator()(MyClass const * lhs, MyClass const * rhs) const {
return lhs->myInt == rhs->myInt && lhs->myMap == rhs->myMap;
}
};
The reason why your solution does not work is because you have effectively written a mechanism to compare a MyClass with a MyClass*, when you actually need something to compare a MyClass* with a MyClass*.
P.S.: My original answer passed the pointers by const&. Thinking about it, that's a strange coding style, so I changed it to pass the pointers by value.
typedef MyClass* PtrMyClass;
struct PointedObjCompare
{ // functor for operator==
bool operator()(const PtrMyClass& lhs, const PtrMyClass& rhs) const
{
// your code goes here
}
};
std::unordered_set < MyClass*, PointedObjHash, PointedObjCompare > myset;
struct classcomp ;
typedef struct basic{
int a ;
set<base*,classcomp> b ;
int c ;
} base ;
classcomp{
bool operator() (const base& *lhs, const base& *rhs) const{
return (*lhs).a < (*rhs).a;}
};
I want to create a set of pointers of datatype base with comparator function classcomp .where does my code gone wrong.Someone please help
From all I see in your code, you've several places where you're trying to use dependent declarations that don't exist yet. Fixing the various problems, one way to do this is:
struct base; //forward decl announces this will exist (sooner or later)
struct classcomp
{
// uses forward decl from before in arguments. since we're
// using pointers, no other type info is required. we don't
// actually implement this yet (we can't, we don't know what
// "base" really is yet).
bool operator ()(const base* lhs, const base* rhs) const;
};
// now we define "base". when the set is declared we provide it a
// custom comparator type that has yet to be fully fleshed out, but
// that's ok. we know what it *will* look like (it provides the
// proper operator() overload).
struct base
{
int a;
std::set<base*, classcomp> b ;
int c;
};
// now we know what a "base" looks like. we can use that to
// implement the comparator operator () and finish what we
// started from before.
inline bool classcomp::operator()(const base* lhs, const base* rhs) const
{
return lhs->a < rhs->a;
}
From there, you can use base as-is or derive from it and shove shove pointers of either into the b collection of a given base (which I wouldn't do, as I would have foisted all of this using smart pointers, but that's another issue separate from this question).
Nested Comparator
This can get considerably simpler if you nest the comparator within base in the first place, and you may want to consider that. In doing so it bring everything you need in one place:
struct base
{
struct cmp_ptr
{
bool operator()(const base* lhs, const base* rhs) const
{
return lhs->a < rhs->a;
}
};
int a;
std::set<base*, cmp_ptr> b ;
int c;
};
Personally, I prefer the latter. If you need to use the comparator type somewhere else, it can be acquired using base::cmp_ptr, which is much clearer (to me at least) in its intent.
Hope it helps.
classcomp {...}; should be struct classcomp{...}; and add a forward declaration of struct base or class base.
Or change the first template parameter of std::set to basic if you intend to do the same.
Also the type classcomp is not complete when you use it. Ensure that struct classcomp definition is available before class basic.
Offtopic but you can better re-write your classcomp less cryptic as:
struct classcomp {
bool operator() (const base *lhs, const base *rhs) const {
return lhs->a < rhs->a;
}
};
Define it this way
struct classcomp {
bool operator() (const base& *lhs, const base& *rhs) const {
return (*lhs).a < (*rhs).a;
}
};
struct base {
int a;
set<base *, classcomp> b;
int c;
};
Could someone explain me what is going on in this example here?
They declare the following:
bool fncomp (int lhs, int rhs) {return lhs<rhs;}
And then use as:
bool(*fn_pt)(int,int) = fncomp;
std::set<int,bool(*)(int,int)> sixth (fn_pt)
While the example for the sort method in algorithm library here
can do like this:
bool myfunction (int i,int j) { return (i<j); }
std::sort (myvector.begin()+4, myvector.end(), myfunction);
I also didn't understand the following:
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
I was trying to make a set of C-style string as follows:
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
set <wrap, compare> myset;
I thought I could create a set defining my sorting function in a similar as when I call sort from algorithm library... once it didn't compile I went to the documentation and saw this syntax that got me confused... Do I need to declare a pointer to a function as in the first example i pasted here?
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
Defines a functor by overloading the function call operator. To use a function you can do:
int main() {
std::set <wrap, bool (*)(wrap,wrap)> myset(compare);
return 0;
}
Another alternative is to define the operator as a part of the wrap class:
struct wrap {
char grid[7];
bool operator<(const wrap& rhs) const {
return strcmp(this->grid, rhs.grid) == -1;
}
};
int main() {
wrap a;
std::set <wrap> myset;
myset.insert(a);
return 0;
}
You're almost there... here's a "fixed" version of your code (see it run here at ideone.com):
#include <iostream>
#include <set>
#include <cstring>
using namespace std;
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2) // more efficient: ...(const wrap& e1, const wrap# w2)
{
return strcmp(w1.grid, w2.grid) < 0;
}
set <wrap, bool(*)(wrap, wrap)> myset(compare);
int main() {
wrap w1 { "abcdef" };
wrap w2 { "ABCDEF" };
myset.insert(w1);
myset.insert(w2);
std::cout << myset.begin()->grid[0] << '\n';
}
"explain [to] me what is going on in this example"
Well, the crucial line is...
std::set<wrap, bool(*)(wrap, wrap)> myset(compare);
...which uses the second template parameter to specify the type of function that will perform comparisons, then uses the constructor argument to specify the function. The set object will store a pointer to the function, and invoke it when it needs to compare elements.
"the example for the sort method in algorithm library..."
std::sort in algorithm is great for e.g. vectors, which aren't automatically sorted as elements are inserted but can be sorted at any time. std::set though needs to maintain sorted order constantly, as the logic for inserting new elements, finding and erasing existing ones etc. all assumes the existing elements are always sorted. Consequently, you can't apply std::sort() to an existing std::set.
"this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
operator()(...) can be invoked on the object using the same notation used to call a function, e.g.:
classcomp my_classcomp;
if (my_classcomp(my_int1, my_int_2))
std::cout << "<\n";
As you can see, my_classcomp is "called" as if it were a function. The const modifier means that the code above works even if my_classcomp is defined as a const classcomp, because the comparison function does not need to modify any member variables of the classcomp object (if there were any data members).
You almost answered your question:
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
struct wrap_comparer
{
bool operator()(const wrap& _Left, const wrap& _Right) const
{
return strcmp(_Left.grid, _Right.grid) == -1;
}
};
// declares pointer to function
bool(*fn_pt)(wrap,wrap) = compare;
// uses constructor with function pointer argument
std::set<wrap,bool(*)(wrap,wrap)> new_set(fn_pt);
// uses the function directly
std::set<wrap,bool(*)(wrap,wrap)> new_set2(compare);
// uses comparer
std::set<wrap, wrap_comparer> new_set3;
std::sort can use either a function pointer or a function object (http://www.cplusplus.com/reference/algorithm/sort/), as well as std::set constructor.
const modifier after function signature means that function can't modify object state and so can be called on a const object.
According to cppreference.com, std::shared_ptr provides a full set of relative operators (==, !=, <, ...), but the semantics of comparison aren't specified. I assume they compare the underlying raw pointers to the referenced objects, and that std::weak_ptr and std::unique_ptr do the same.
For some purposes, I would prefer to have relative operators that order the smart pointers based on comparing the referenced objects (rather than the pointers to them). This is already something I do a lot, but with my own "dumb pointers" that behave mostly like raw pointers except for the relative operators. I'd like to do the same thing with the standard C++11 smart pointers too. So...
Is it OK to inherit from the C++11 smart pointers (shared_ptr, weak_ptr and unique_ptr) and override the relative operators?
Are there any sneaky issues I need to look out for? For example, are there any other methods I need to implement or use using for to ensure things work correctly?
For the ultimate in laziness, is there a library template available that will do this for me automatically?
I'm hoping this is an "of course you can do that, idiot!" kind of thing, but I'm a little uncertain because there are some classes in the standard library (containers like std::map at least) that you're not supposed to inherit from.
The first thing, as others have already pointed out is that inheritance is not the way to go. But rather than the convoluted wrapper suggested by the accepted answer, I would do something much simpler: Implement your own comparator for your own types:
namespace myns {
struct mytype {
int value;
};
bool operator<( mytype const& lhs, mytype const& rhs ) {
return lhs.value < rhs.value;
}
bool operator<( std::shared_ptr<mytype> const & lhs, std::shared_ptr<mytype> const & rhs )
{
// Handle the possibility that the pointers might be NULL!!!
// ... then ...
return *lhs < *rhs;
}
}
The magic, which is not really magic at all is Argument Dependent Lookup (aka. Koening Lookup or ADL). When the compiler encounters a function call it will add the namespace of the arguments to lookup. If the objects are the instantiation of a template, then the compiler will also add the namespaces of the types used to instantiate the template. So in:
int main() {
std::shared_ptr<myns::mytype> a, b;
if ( a < b ) { // [1]
std::cout << "less\n";
} else {
std::cout << "more\n";
}
}
In [1], and because a and b are objects user defined types (*) ADL will kick in and it will add both std and myns to the lookup set. It will then find the standard definition of operator< for std::shared_ptr that is:
template<class T, class U>
bool std::operator<(shared_ptr<T> const& a, shared_ptr<U> const& b) noexcept;
And it will also add myns and add:
bool myns::operator<( mytype const& lhs, mytype const& rhs );
Then, after lookup finishes, overload resolution kicks in, and it will determine that myns::operator< is a better match than std::operator< for the call, as it is a perfect match and in that case non-templates take preference. It will then call your own operator< instead of the standard one.
This becomes a bit more convoluted if your type is actually a template, if it is, drop a comment and I will extend the answer.
(*) This is a slight simplification. Because operator< can be implemented both as a member function or a free function, the compiler will check inside std::shared_ptr<> for member operator< (not present in the standard) and friends. It will also look inside mytype for friend functions... and so on. But at the end it will find the right one.
In general, it's not safe to inherit from anything who's destructor is not dynamic. It can be and is done commonly, you just have to be really careful.
Instead of inheriting from the pointers, I'd just use composition, especially since the number of members is relatively small.
You might be able to make a template class for this
template<class pointer_type>
class relative_ptr {
public:
typedef typename std::pointer_traits<pointer_type>::pointer pointer;
typedef typename std::pointer_traits<pointer_type>::element_type element_type;
relative_ptr():ptr() {}
template<class U>
relative_ptr(U&& u):ptr(std::forward<U>(u)) {}
relative_ptr(relative_ptr<pointer>&& rhs):ptr(std::move(rhs.ptr)) {}
relative_ptr(const relative_ptr<pointer>& rhs):ptr(std::move(rhs.ptr)) {}
void swap (relative_ptr<pointer>& rhs) {ptr.swap(rhs.ptr);}
pointer release() {return ptr.release();}
void reset(pointer p = pointer()) {ptr.reset(p);}
pointer get() const {return ptr.get();}
element_type& operator*() const {return *ptr;}
const pointer_type& operator->() const {return ptr;}
friend bool operator< (const relative_ptr& khs, const relative_ptr& rhs) const
{return std::less<element>(*lhs,*rhs);}
friend bool operator<=(const relative_ptr& khs, const relative_ptr& rhs) const
{return std::less_equal<element>(*lhs,*rhs);}
friend bool operator> (const relative_ptr& khs, const relative_ptr& rhs) const
{return std::greater<element>(*lhs,*rhs);}
friend bool operator>=(const relative_ptr& khs, const relative_ptr& rhs) const
{return std::greater_equal<element>(*lhs,*rhs);}
friend bool operator==(const relative_ptr& khs, const relative_ptr& rhs) const
{return *lhs==*rhs;}
friend bool operator!=(const relative_ptr& khs, const relative_ptr& rhs) const
{return *lhs!=*rhs;}
protected:
pointer_type ptr;
};
Obviously, the simplicity of the wrapper reduces you to the lowest common denominator for the smart pointers, but whatever. They're not exactly complicated, you could make one for each of the smart pointer classes.
I will provide a warning that I don't like the way == works, since it may return true for two pointers to different objects. But whatever. I also haven't tested the code, it might fail for certain tasks, like attempting to copy when it contains a unique_ptr.
It's hazardous to inherit from any class that supports assignment and copy-construction, due to the risk of cutting a derived-class instance in half by accidentally assigning it to a base-class variable. This affects most classes, and is pretty much impossible to prevent, thus requiring vigilance on the part the class's users whenever they copy instances around.
Because of this, classes intended to function as bases usually shouldn't support copying. When copying is necessary, they should provide something like Derived* clone() const override instead.
The problem you are trying to solve is probably best addressed by leaving things as they are and providing custom comparators when working with such pointers.
std::vector<std::shared_ptr<int>> ii = …;
std::sort(begin(ii), end(ii),
[](const std::shared_ptr<int>& a, const std::shared_ptr<int>& b) {
return *a < *b;
});