-3

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.

Deepti Jain
  • 1,901
  • 4
  • 21
  • 29
  • 1
    Most of the code is irrelevant. The problem is that `Fruit` does not satisfy the requirements to instantiate an `std::set`. There are plenty of SO questions on that. – juanchopanza Dec 10 '13 at 21:47
  • 1
    I did not downvote you nor do I think its really appropriate here but if you want to spare the scorn of other SO users try and narrow your code down to the smallest piece that still produces the error. in this case it would be class Fruit { }; std::set – odinthenerd Dec 10 '13 at 22:05

1 Answers1

2

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.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • I think the OP may need an easier example. Make the bool operator< (Fruit const& f0, Fruit const& f1) { return false; } actually compare something in Fruit (so he can get more than one of them into his set) and I will delete my answer because your is otherwise better. – odinthenerd Dec 10 '13 at 22:15
  • @PorkyBrain: done. I like to define a strict weak order where all objects are part of same equality class, though... – Dietmar Kühl Dec 10 '13 at 22:37