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) {}
Related
#include <iostream>
using namespace std;
class Test{
private: int cost{};
public:
Test(int value): cost{value}{}
int GetCost() const{return cost;}
bool operator >(const Test& rhs) const noexcept {return GetCost() > rhs.GetCost();}
bool operator>=(const Test& rhs) const noexcept {return (*this > rhs) || (*this == rhs;)}
};
bool operator==(const Test& a, const Test& b) noexcept
{
return a.GetCost() == b.GetCost();
}
int main()
{
cout<< (Test{2} >= Test{3});
return 0;
}
I get the following error for the code.
error: no match for ‘operator==’ (operand types are ‘const Test’ and ‘const Test’)
bool operator>=(const Test& rhs) const noexcept {return *this == rhs;}
What is the problem here? Is it not possible to mix member and non-member operator overloads?
You should also overload operator == in Test class.
class Test{
private:
int cost{};
public:
Test(int value): cost{value}{}
int GetCost() const{return cost;}
bool operator==(const Test& rhs) const noexcept {return GetCost() == rhs.GetCost();}
bool operator >(const Test& rhs) const noexcept {return GetCost() > rhs.GetCost();}
bool operator>=(const Test& rhs) const noexcept {return (*this > rhs) || (*this == rhs);}
};
So you can use it in your overloading and member functions like operator>=.
for more work you can do this:
bool operator==(const Test& a, const int& b) noexcept
{
return a.GetCost() == b;
}
And This:
bool operator==(const int& a, const Test& b) noexcept
{
return a == b.GetCost();
}
By doing so you can even use == as follows:
cout<< (Test{3} == 3);
cout<< (2 == Test{3});
I have trying to use a class as a key in an std::map. I have read documentations and I know I have to type some sort of sorting rule because my std::map is a binary search tree. The problem arise because the class key have another classes in it. Can someone get me some advice how to build the operators?
MasterRenderer file
std::map<TexturedModel, std::vector<Entity>> entites;
void MasterRenderer::processEntity(Entity entity)
{
TexturedModel model = entity.getModel();
auto search = entites.find(model);
if (search != entites.end()) {
//found
entites[model].emplace_back(entity);
}
else {
//not found
entites[model].emplace_back(entity);
}
std::cout << entites[model].size() << std::endl;
}
TexturedModel.h
TexturedModel(RawModel model, ModelTextures
texture)
:m_model(model), m_texture(texture) {
};
friend bool operator<(const TexturedModel& m,
const
TexturedModel& m2) {
return m.m_model < m2.m_model || m.m_model ==
m2.m_model && m.m_texture < m2.m_texture;
}
private:
RawModel m_model;
ModelTextures m_texture;
};
Rawmodel.h
unsigned int VaoID;
unsigned int Vertecies;
RawModel(unsigned int vaoID, unsigned int
vertecies)
:VaoID(vaoID), Vertecies(vertecies) {};
friend bool operator <(const RawModel& rhs, const
RawModel& rhs2)
{
return rhs.get() < rhs2.get();
}
friend bool operator ==(const RawModel& rhs, const
RawModel& rhs2)
{
return rhs.get() == rhs2.get();
}
const RawModel* get() const {
return this;
}
ModelTextures.h
ModelTextures(unsigned int ID)
:textureID(ID) {};
friend bool operator<(const ModelTextures& rhs,
const ModelTextures& rhs2)
{
return rhs.get() < rhs2.get();
}
const ModelTextures* get() const{
return this;
}
private:
unsigned int textureID;
float shineDamper = 1.0f;
float reflectivity = 0.0f;
};
friend bool operator<(const ModelTextures& rhs,
const ModelTextures& rhs2)
{
return rhs.get() < rhs2.get();
}
const ModelTextures* get() const{
return this;
}
this orders by address of the object, not content. That violates the requirements of std::map.
friend auto as_tie(const ModelTexture& m) {
return std::tie(m.textureID, m.shineDamper, m.reflexivity);
}
friend bool operator<(const ModelTextures& rhs,
const ModelTextures& rhs2)
{
return as_tie(rhs) < as_tie(lhs);
}
repeat this pattern for TexturedModel and RawModel.
If you are stuck in c++11 you have to manually write the return type of as_tie or use decltype.
friend auto as_tie(const ModelTexture& m)
-> decltype(std::tie(m.textureID, m.shineDamper, m.reflexivity))
{
return std::tie(m.textureID, m.shineDamper, m.reflexivity);
}
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'm writing my own typesafe enum header-only library at https://bitbucket.org/chopsii/typesafe-enums
The idea is to replace the non-type-safe c-style enum like:
enum ItemCategory
{
BLOCK,
WEAPON
};
with something that's properly type safe.
So far, my solution uses a macro that, for an example equivalent to the above enum, looks like this:
TypesafeEnum(ItemCategory,
(BLOCK)
(WEAPON)
);
And expands to something that looks like this:
template<typename InnerType>
class Wrapped {
public:
InnerType getValue() const { return _val; }
bool operator<(const Wrapped<InnerType>& rhs) const { ; return _val < rhs._val; }
bool operator>(const Wrapped<InnerType>& rhs) const { ; return _val > rhs._val; }
bool operator==(const Wrapped<InnerType>& rhs) const { ; return _val == rhs._val; }
private:
InnerType _val;
protected:
explicit Wrapped<InnerType>(const InnerType& val) : _val(val) {}
void setValue(const InnerType& val) { _val = val; }
};
class WrappedTypeItemCategory : private Wrapped<int>
{
private:
typedef const std::string* strptr;
typedef const std::string* const cstrptr;
explicit WrappedTypeItemCategory(const std::string& label, int val): Wrapped<int>(val), str(&label)
{}
cstrptr str;
public:
static WrappedTypeItemCategory make(const std::string& label, int val)
{
return WrappedTypeItemCategory(label, val);
}
void operator=(const WrappedTypeItemCategory& rhs)
{
;
setValue(rhs.getValue());
const_cast<strptr>(str) = rhs.str;
}
int getValue() const
{
return Wrapped<int>::getValue();
}
const std::string& getString() const
{
return *str;
}
bool operator<(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() < rhs.getValue();
}
bool operator>(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() > rhs.getValue();
}
bool operator==(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() == rhs.getValue();
}
friend std::ostream & operator<<(std::ostream &os, const WrappedTypeItemCategory& rhs)
{
;
return os << *rhs.str << "(" << rhs.getValue() << ")";
}
};
;
namespace {
template<typename T> class ItemCategoryInner : public TypesafeEnumBase
{
public:
static const WrappedTypeItemCategory BLOCK;
static const WrappedTypeItemCategory WEAPON;
static const std::string BLOCKStr;
static const std::string WEAPONStr;
};
template<typename T> const WrappedTypeItemCategory ItemCategoryInner<T>::BLOCK = WrappedTypeItemCategory::make(ItemCategoryInner<T>::BLOCKStr, 0);
template<typename T> const WrappedTypeItemCategory ItemCategoryInner<T>::WEAPON = WrappedTypeItemCategory::make(ItemCategoryInner<T>::WEAPONStr, 1);
template<typename T> const std::string ItemCategoryInner<T>::BLOCKStr("ItemCategory::BLOCK");
template<typename T> const std::string ItemCategoryInner<T>::WEAPONStr("ItemCategory::WEAPON");
struct ItemCategoryTemplateConstantTrick
{};
};
class ItemCategory : public ItemCategoryInner<ItemCategoryTemplateConstantTrick>
{
private:
const WrappedTypeItemCategory* const val;
public:
class InvalidValueError : public std::runtime_error
{
public:
const int val;
InvalidValueError(int val): std::runtime_error(std::string("Invalid value given for ") + "ItemCategory::make"), val(val)
{}
};
ItemCategory(const WrappedTypeItemCategory& value): val(&value)
{}
void operator=(const ItemCategory& rhs)
{
const_cast<const WrappedTypeItemCategory*>(val) = rhs.val;
}
static ItemCategory make(const int& val)
{
if (val == ItemCategory::BLOCK.getValue()) return ItemCategory(ItemCategory::BLOCK);
if (val == ItemCategory::WEAPON.getValue()) return ItemCategory(ItemCategory::WEAPON);
;
throw InvalidValueError(val);
}
const WrappedTypeItemCategory* const getWrappedValue() const
{
return val;
}
int getValue() const
{
return val->getValue();
}
const std::string & getString() const
{
return val->getString();
}
bool operator<(const ItemCategory& rhs) const
{
return *val < *rhs.val;
}
bool operator>(const ItemCategory& rhs) const
{
return *val > *rhs.val;
}
bool operator==(const WrappedTypeItemCategory& rhs) const
{
return *val == rhs;
}
bool operator!=(const WrappedTypeItemCategory& rhs) const
{
return !(*val == rhs);
}
bool operator<=(const WrappedTypeItemCategory& rhs) const
{
return (*val == rhs || *val < rhs);
}
bool operator>=(const WrappedTypeItemCategory& rhs) const
{
return (*val == rhs || *val > rhs);
}
void print(std::ostream& os) const override
{
os << *val;
}
friend std::ostream & operator<<(std::ostream &os, const ItemCategory& rhs)
{
rhs.print(os);
return os;
}
};
;
If I manually pre-expand it, like I have done here - by pre-compiling to file - then intellisense handles it all up until the line that says:
class ItemCategory : public ItemCategoryInner<ItemCategoryTemplateConstantTrick>
At which point it starts thinking ItemCategoryInner and ItemCategoryTemplateConstantTrick are ambiguous, along with many other things on every few lines of the file.
The header that contains this code is included in many places. I know I'm violating the One Definition Rule, which is why I'm using the Template Constant Trick, but I think I need to violate the ODR as my goal is to have an easy to use Macro based typesafe replacement for C++ enums.
I'm not sure if it's the violation of ODR that is the cause of my issues, or something else. I tried __declspec(selectany) but it didn't seem to help - and I would prefer if this macro would be eventually cross-platform, because if it works out, I have other projects I would use it in.
Either way, the .cpp files etc that make use of the enum are able to, and intellisense correctly suggests the options.
However, on a possibly related note, if I don't pre-expand the macro, intellisense isn't able to parse it and it doesn't know what a ItemCategory is at all, even though it compiles and works fine.
I just want my intellisense to work properly with my typesafe enums - it slows down intellisense and confuses it in other code in the same project.
Sorry if the question title terminology is wrong, but here is what I want to do.I need to sort a vector of objects, but contrary to a typical comparison "less than" approach I need to re-position the objects based on some string ID property so that each same type members are positioned in consecutive order like this:
[id_town,id_country,id_planet,id_planet,id_town,id_country]
becomes this:
[id_town,id_town,id_country,id_country,id_planet,id_planet]
id_ property is string.
std::sort has a third parameter which can be used to pass a boolean predicate that acts as custom comparator. Write your own comparator acording to your specifications and use it.
For example:
struct foo
{
std::string id;
foo(const std::string& _id) : id( _id ) {}
};
//Functor to compare foo instances:
struct foo_comparator
{
operator bool(const foo& lhs , const foo& rhs) const
{
return lhs.id < rhs.id;
}
};
int main()
{
std::vector<foo> v;
std::sort( std::begin(v) , std::end(v) , foo_comparator );
}
Also, in C++11 you could use a lambda:
std::sort( std::begin(v) , std::end(v) , [](const foo& lhs , const foo& rhs) { return lhs.id < rhs.id; } );
Finally, you can also overload the comparison operators (operator> and operator<) and use comparators provided by the standard library like std::greater:
struct foo
{
std::string id;
foo(const std::string& _id) : id( _id ) {}
friend bool operator<(const foo& lhs , const foo& rhs)
{
return lhs.id < rhs.id;
}
friend bool operator>(const foo& lhs , const foo& rhs)
{
return rhs < lhs;
}
friend bool operator>=(const foo& lhs , const foo& rhs)
{
return !(lhs < rhs);
}
friend bool operator<=(const foo& lhs , const foo& rhs)
{
return !(lhs > rhs);
}
};
int main()
{
std::vector<foo> v;
std::sort( std::begin(v) , std::end(v) , std::greater );
}