A generic set operations class i.e. intersection, union, minus etc - c++

I want to write a C++ class that offers set operations that work on vectors of strings and vectors of my own data type. Are there any easy ways of doing this rather than writing a different function for each data type? So far I have written operations for string vectors. Below shows an example of my set union:
vector<string> SetOperations::set_union(vector<string> set1,
vector<string> set2) {
for(std::vector<int>::size_type i = 0; i < set1.size(); i++) {
set1.push_back(set2.at(i));
}
return set1;
}
So I want the same thing again but where string is say my_data_type which is a struct of various members. Let's say it looks like this:
struct my_data_type {
int label;
vector<string> x;
vector<string> y;
string str;
};
A function for each data type would also not be as simple as my set_union(...) function because surely I would need to test for equality on each member of my_data_type in the case of set intersection?
Also, I'm quite new to C++ so any comments on my existing function would be appreciated too.
Many thanks.

Some of these already exist and are in the algorithm header:
set_union
set_difference
set_intersection
set_symmetric_difference
These all support a comparator function so that you could do it to all your own data types. Or as posted in the other reply make your Containers comply to the STL requirements.
See: http://www.cplusplus.com/reference/algorithm/

There are already such algorithms (union, intersection, sorting, ...): http://www.cplusplus.com/reference/algorithm/
Your elements simply need to meet the requirements for STL container elements (see http://msdn.microsoft.com/fr-fr/library/bb385469.aspx):
All reference types that are inserted
into STL/CLR containers must have, at
a minimum, the following elements:
A public copy constructor.
A public assignment operator.
A public destructor.
Furthermore, associative containers
such as set and map must have a public
comparison operator defined, which is
operator< by default. Some operations
on containers might also require a
public default constructor and a
public equivalence operator to be
defined.
Like reference types, value types and
handles to reference types that are to
be inserted into an associative
container must have a comparison
operator such as operator< defined.
The requirements for a public copy
constructor, public assignment
operator, and a public destructor do
not exist for value types or handles
to reference types.
You can find information about operator overloading (to be implemented in your custom class) on that WikiBook: http://en.wikibooks.org/wiki/C++_Programming/Operators/Operator_Overloading

Related

Is it possible to declare an inherited class member as private?

I'm dealing with an ambiguity problem when overloading the bracket operator with operator[](int) and operator[](std::vector<int>), and using an instance of a class B::public A() that has both bool and std::vector<int> cast operators defined (the issue lies in the boolean cast that can be implicitely converted to int and thus creates ambiguity).
Since the bool cast operator is inherited from A(), is there a way to define it as private within the definition of B()?
CONTEXT:
I'm overloading a storage container class (a simple wrapper around std::vector) and B is a "comparer" class that compares containers value-by-value, and can be cast to a simple bool value for bulk comparison, or std::vector<int> with index positions for true values. This allows me to both perform simple bulk comparisons e.g. if(mat1=mat2) {}as well as apply index masks to my containers e.g. mat1[mat1==14] = // ....

How to use insert in the set in c++ for user defined data type?

I would like to use a set<vector<data>> where data is a user-defined class and both the set and the vector are STL,
class data
{
int info;
};
I am not able to understand whether we need to define comparator operator for both vector<data> and data class or only data class.
And how do we define the comparator operator for the same?
std::vector already has an ordering - lexicographical order - so you normally don't need to do anything with that.
You always need to define an ordering for your own classes if you use the default vector ordering (see example below for a case where you don't need to), and the most common way is to overload operator<.
Note that the ordering relation must be a strict weak ordering, or using the set is undefined.
If you want a special sense of "equality" for the set, you need to define your own.
For example, this code would make a set where vectors of equal length are considered equal (so only the first one encountered of each length is added to the set):
template<typename T>
struct shorter_vector
{
bool operator() (const std::vector<T>& left, const std::vector<T>& right) const
{
return left.size() < right.size();
}
};
// ...
struct A { int x; };
std::set<std::vector<A>, shorter_vector<A>> samelengths;
samelengths.insert({A{1}});
samelengths.insert({A{2}});
samelengths.insert({A{3},A{4}});
samelengths.insert({A{5},A{67}});
// set now contains {A{1}} and {A{3},A{4}}
Note that this set doesn't need an ordering for the vector's elements, since the equivalence relation is defined on structure alone.

Declaration of function to generate vector of class with a vector of another class

I have 2 classes:
class Item;
class Component;
And I have a function to generate a vector of Items from a vector of Components
static vector<Item> generateItemsFromComponents( vector<Component> components )
{
vector<Item> vi;
// ...
return vi;
}
My question is, where do I declare this function? In the Item class or in the Component class? Or the class design is wrong? Maybe suggest better methods to achieve this?
First, if there is a 1 to 1 conversion function from a Component to an Item, you should consider :
Having an explicit conversion method in Component, e.g. Item ToItem();, or
Having an implicit conversion operator, but this is unlikely unless automatic conversion (by the compiler) would make sense in any case.
e.g :
operator const Item&() const;
Once you have the single conversion function available, the method that operates on containers does not really belong to any of your existing interfaces (unless you have a class for containers of Components already), so it makes sense to declare a free function :
Example:
vector<Item> generateItemsFromComponents( const vector<Component>& components )
{
vector<Item> vi;
for(auto& comp in components)
vi.push_back(comp.ToItem());
return vi;
}
However, as pointed by #chris, using std::transform directly, together with a lambda, makes things easier:
Example:
vector<Item> vi;
transform(begin(comp), end(comp), back_inserter(vi), [](Component c){ return c.ToItem(); }
The advantages of this last method:
You are not dependent on the container type : you operate on iterators, comp could be any standard compliant container, not just a vector.
It might be more efficient, and is definitely very readable
Answer:
Provide a single conversion function in the Component class
Use the standard <algorithm> together with this function
Consider lambdas as a substitute for very simple functions
If a function is not related to a type, just make it a free function

unordered_multimap usage and operator overwriting

I need to use an unordered_multimap for my Note objects and the keys will be the measureNumber member of my objects. I'm trying to implement it as shown here but I'm stuck.
First off, I don't understand why I have to overwrite the operator== before I can use it. I'm also confused about why I need a hash and how to implement it. In this example here, none of those two things is done.
So based on the first example, this is what I have:
class Note {
private:
int measureNumber;
public:
inline bool operator== (const Note &noteOne, const Note &noteTwo);
}
inline bool Note::operator ==(const Note& noteOne, const Note& noteTwo){
return noteOne.measureNumber == noteTwo.measureNumber;
}
I don't know how to implement the hash part though. Any ideas?
std::multimap is based on a sorted binary tree, which uses a less-than operation to sort the nodes.
std::unordered_multimap is based on a hash table, which uses hash and equality operations to organize the nodes without sorting them.
The sorting or hashing is based on the key values. If the objects are the keys, then you need to define these operations. If the keys are of predefined type like int or string, then you don't need to worry about it.
The problem with your pseudocode is that measureNumber is private, so the user of Note cannot easily specify the key to the map. I would recommend making measureNumber public or rethinking the design. (Is measure number really a good key value? I'm guessing this is musical notation.)
std::multimap< int, Note > notes;
Note myNote( e_sharp, /* octave */ 3, /* measure */ 5 );
notes.insert( std::make_pair( myNote.measureNumber, myNote ) );
The objects can be keys and values at the same time, if you use std::multiset or std::unordered_multiset, in which case you would want to define the operator overload (and possibly hash). If operator== (or operator<) is a member function, then the left-hand side becomes this and the right-hand side becomes the sole argument. Usually these functions should be non-member friends. So then you would have
class Note {
private:
int measureNumber;
public:
friend bool operator< (const Note &noteOne, const Note &noteTwo);
}
inline bool operator <(const Note& noteOne, const Note& noteTwo){
return noteOne.measureNumber < noteTwo.measureNumber;
}
This class could be used with std::multiset. To perform a basic lookup, you can construct a dummy object with uninitialized values except for measureNumber — this only works for simple object types.
I need to use an unordered_multimap for my Note objects and the keys
will be the measureNumber member of my objects.
OK - I'm not sure whether you're after a multiset, unordered_multiset, multimap, or unordered_multimap. I know your title refers to unordered_multimap, but the link you provided leads to unordered_multiset. There are a multitude of considerations which should be taken into account when choosing a container, but second-guessing which will be the best-performing without profiling is a risky business.
I don't understand why I have to overwrite the operator== before I can use it.
I'm also confused about why I need a hash and how to implement it.
In this example here, none of those two things is done.
You need the operator== and std::hash as they're used internally by unordered_multimap and unordered_multiset. In the example you linked to, the key is of type int, so operator== and std::hash<int> are already defined. If you choose to use Note as a key, you have to define these yourself.
I'd recommend starting with a multiset if you don't need to change the elements frequently. If you do want to be able to change Notes without erasing and inserting, I'd recommend removing measureNumber as a member of Note and using a multimap<int, Note>.
If you feel an unordered_ version of your container would better suit your needs, you still have the set vs map choice. If you choose unordered_multimap<int, Note> (having removed measureNumber from Note), then as in your linked example, the key is int. So you won't have to define anything special for this to work. If you choose to keep measureNumber as a member of Note and use unordered_multiset<Note>, then Note is the key and so you need to do further work, e.g.
#include <functional>
#include <unordered_set>
class Note; // Forward declaration to allow specialisation of std::hash<>
namespace std {
template<>
class hash<Note> {
public:
size_t operator()(const Note &) const; // declaration of operator() to
// allow befriending by Note
};
}
class Note {
private:
int measureNumber;
public:
// functions befriended to allow access to measureNumber
friend bool operator== (const Note &, const Note &);
friend std::size_t std::hash<Note>::operator()(const Note &) const;
};
inline bool operator== (const Note &noteOne, const Note &noteTwo) {
return noteOne.measureNumber == noteTwo.measureNumber;
}
std::size_t std::hash<Note>::operator()(const Note &note) const {
return std::hash<int>()(note.measureNumber);
}
This lets you create and use std::unordered_multiset<Note>. However, I'm not sure this is really what you need; you could even find that a sorted std::vector<Note> is best for you. Further research and thought as to how you'll use your container along with profiling should give the best answer.

Is the "==" operator required to be defined to use std::find

Let's say I have:
class myClass
std::list<myClass> myList
where myClass does not define the == operator and only consists of public fields.
In both VS2010 and VS2005 the following does not compile:
myClass myClassVal = myList.front();
std::find( myList.begin(), myList.end(), myClassVal )
complaining about lack of == operator.
I naively assumed it would do a value comparison of the myClass object's public members, but I am almost positive this is not correct.
I assume if I define a == operator or perhaps use a functor instead, it will solve the problem.
Alternatively, if my list was holding pointers instead of values, the comparison would work.
Is this right or should I be doing something else?
The compiler does not automatically generate a default operator==(), so if you don't write one yourself, objects of your class can't be compared for equality.
If you want memberwise comparison on the public members you have to implement that yourself as operator==() (or "manually" use a separate function/functor to do the comparison).
Find does require the value to be equality comparable, and the compiler will not define you a default operator==.
Alternatively, you can use find_if and supply a functor predicate.
std::find needs operator==. Even if the members are public, it doesn't necessarily mean that all of them are relevant in defining what equality means for this class.
If you don't want to overload the operator for some reason (e.g there is no one single intuitive meaning of equality for that class, instances could be consider equal in some respect or another), you can code a suitable function object and use std::find_if. For example:
struct same_surname_as
{
Person p;
same_surname_as(const Person& x): p(x) {}
bool operator()(const Person& person) const { return p.surname == person.surname; }
};
list<Person> li;
find(li.begin(), li.end(), same_surname_as(Person("Pu Songling"));