I am trying to compare different classes of objects in C++.
Everything works well if I remove section3. But I'd like to know how to edit the comparative operators == and != to make it work without any errors?
The error that I get is "no match for 'operator==' (operand types are 'Fruit' and 'Plant') "
Here is my code :
#include <iostream>
#include <string>
class Plant
{
public:
Plant(std::string name) : type_(name)
{ }
bool operator==(const Plant &that) const
{ return type_ == that.type_; }
bool operator!=(const Plant &that) const
{ return !operator==(that); }
void print()
{ std::cout << type_ << std::endl; }
protected:
std::string type_;
};
class Fruit: public Plant
{
public:
Fruit(std::string name, std::string taste)
: Plant(name)
, taste_(taste)
{ }
bool operator==(const Fruit& that) const
{
return ( (taste_ == that.taste_) && (Plant::operator==(that)) );
}
bool operator!=(const Fruit& that) const
{
return !operator==(that);
}
void print()
{
Plant::print();
std::cout << taste_ << std::endl;
}
private:
std::string taste_;
};
int main()
{
Plant a("Maple");
a.print();
Plant b("Maple");
if (a == b)
{
std::cout << "a and b are equal" << std::endl;
}
else
{
std::cout << "a and b are not equal" << std::endl;
}
Fruit c("Apple","sweet");
c.print();
Fruit d("Apple","sweet");
if (c == d)
{
std::cout << "c and d are equal" << std::endl;
}
else
{
std::cout << "c and d are not equal" << std::endl;
}
if (a == c)
{
std::cout << "a and c are equal" << std::endl;
}
else
{
std::cout << "a and c are not equal" << std::endl;
}
/* Section 3 */
if (c == a)
{ std::cout <<"c and a are equal\n"<< std::endl; }
else
{ std::cout <<"c and a are not equal\n"<< std::endl; }
if (a != c)
{ std::cout <<"c and a are not equal\n"<< std::endl; }
else
{ std::cout <<"c and a are equal\n"<< std::endl; }
return 0;
}
Thanks ..
You can add non-member functions,
bool operator==(Fruit const& f, Plant const& p)
{
return false;
}
bool operator!=(Fruit const& f, Plant const& p)
{
return !(f == p);
}
This will work for one sub-type of Plant. This approach is not scalable. If you create more sub-types of Plant, you'll need to use a different approach.
You have to either implement comparitor operators to compare Fruit and Plant or downcast the Fruit to a plant in the comparison:
bool operator==(const Plant& plant, const Fruit& fruit) { /* test here */ }
bool operator==(const Fruit& fruit, const Plant& plant) { return (plant == fruit); }
Or if you have pointers:
Fruit* fruit = new Fruit("apple", "sour");
Plant* plant = new Plant("maple");
if(*plant == *static_cast<Plant*>(fruit)) {}
It's bit unclear what you're trying to achieve, but apparently it involves dynamic type checking where two objects compare equal if they dynamically are of some common base type X and are equal according to some criterion specified in that base type.
Finding the common type X is in general a thorny problem, because C++ supports multiple inheritance. But if one assumes, for simplicity, single inheritance, that is, the case where each class has at most one base class, then one can let one of the objects that are involved in a comparison, walk up the base class chain and use e.g. dynamic_cast to check if the other object is of this type, e.g. like this:
#include <string>
#include <utility> // std::move
using Byte_string = std::string;
class Base
{
private:
Byte_string s_;
protected:
virtual
auto equals( Base const& other ) const
-> bool
{ return s_ == other.s_; }
public:
friend
auto operator==( Base const& a, Base const& b )
-> bool
{ return a.equals( b ); }
explicit Base( Byte_string s )
: s_( move( s ) )
{}
};
class Derived
: public Base
{
private:
Byte_string t_;
protected:
auto equals( Base const& other ) const
-> bool override
{
if( auto p_other = dynamic_cast<Derived const*>( &other ) )
{
return Base::equals( other ) and t_ == p_other->t_;
}
return Base::equals( other );
}
public:
Derived( Byte_string s, Byte_string t )
: Base( move( s ) )
, t_( move( t ) )
{}
};
class Most_derived
: public Derived
{
private:
int u_;
protected:
auto equals( Base const& other ) const
-> bool override
{
if( auto p_other = dynamic_cast<Most_derived const*>( &other ) )
{
return Derived::equals( other ) and u_ == p_other->u_;
}
return Derived::equals( other );
}
Most_derived( Byte_string s, Byte_string t, int u )
: Derived( move( s ), move( t ) )
, u_( u )
{}
};
#include <iostream>
using namespace std;
auto main() -> int
{
Base a( "Maple" );
Base b( "Maple" );
cout << "a and b are " << (a == b? "" : "not ") << "equal.\n";
Derived c( "Apple", "sweet" );
Derived d( "Apple", "sweet" );
cout << "c and d are " << (c == d? "" : "not ") << "equal.\n";
cout << "a and c are " << (a == c? "" : "not ") << "equal.\n";
cout << "c and a are " << (c == a? "" : "not ") << "equal.\n";
Base& x = d;
cout << "x and c are " << (x == c? "" : "not ") << "equal.\n";
cout << "c and x are " << (c == x? "" : "not ") << "equal.\n";
}
Related
I have the following class hierarchy plus a wrapper class:
struct Base {
virtual ~Base() = default;
virtual void init() = 0;
};
struct DerivedA : Base {
void init() override {
// do something specific to DerivedA
}
};
struct DerivedB : Base {
void init() override {
// do something specific to DerivedB
}
};
struct Wrapper{
Base* _item;
};
Now, I would like to have a container of Wrapper (e. g. std::vector<Wrapper>) and a function f which expects two derived classes as parameters, like this:
void f(DerivedA* d1, DerivedA* d2) {
std::cout << "A vs A" << std::endl;
}
void f(DerivedA* d1, DerivedB* d2) {
std::cout << "A vs B" << std::endl;
}
void f(DerivedB* d1, DerivedB* d2) {
std::cout << "B vs B" << std::endl;
}
void f(DerivedB* d1, DerivedA* d2) {
std::cout << "B vs A" << std::endl;
}
int main() {
std::vector<Wrapper> items;
items.emplace_back(new DerivedA());
items.emplace_back(new DerivedB());
items.emplace_back(new DerivedB());
items.emplace_back(new DerivedA());
for (auto i = 0; i < items.size(); i++) {
for (auto j = i+1; j < items.size(); j++) {
// do something wizh all item pairs
f(items[i]._item, items[j]._item);
}
}
return 0;
}
This does not compile because the compiler does expect a declaration of f(Base* b1, Base* b2), which of course makes sense.
What I tried so far:
Use enum class DerivedType { A, B, C }; to decipher the type of the derived class. This works but seems clumsy.
Use a templated version of Wrapper and store each template type in a different container, like std::vector<Wrapper<DerivedX>>. Again, this feels like I'm doing something wrong.
Use std::variant<DerivedA, DerivedB> _item in place of Base* _item in Wrapper. But I could not get std::visit to return a pointer to a dervied class the way I require it for f (because operator()(DerivedX* d) expects the same return to for any X).
What are possible ways to implement this? Does the solution I am looking for even exist?
Using an RTTI-based solution is also an option:
void f1(Base* d1, Base* d2) {
const auto& t1 = typeid(*d1);
const auto& t2 = typeid(*d2);
if (t1 == typeid (DerivedA) && t2 == typeid (DerivedA)) {
std::cout << "A vs A" << std::endl;
}
if (t1 == typeid (DerivedA) && t2 == typeid (DerivedB)) {
std::cout << "A vs B" << std::endl;
}
if (t1 == typeid (DerivedB) && t2 == typeid (DerivedA)) {
std::cout << "B vs A" << std::endl;
}
if (t1 == typeid (DerivedB) && t2 == typeid (DerivedB)) {
std::cout << "B vs B" << std::endl;
}
}
With std::variant, it would be:
struct Wrapper{
std::variant<DerivedA, DerivedB> _item;
};
int main() {
std::vector<Wrapper> items {
{ DerivedA()},
{ DerivedB()},
{ DerivedB()},
{ DerivedA()}
};
for (auto i = 0; i < items.size(); i++) {
for (auto j = i+1; j < items.size(); j++) {
// do something with all item pairs here
std::visit([] (auto& lhs, auto& rhs) { f(&lhs, &rhs); }, items[i]._item, items[j]._item);
}
}
}
What's the consistency of the following codes:-
TMAP.h
#include <algorithm>
#include <map>
template <class K, class V>
class TMAP
{
private:
std::map <K,V> map_K_V;
public:
bool key_exists(const K& key) { return map_K_V.count( key ) > 0; }
bool insert(const K& key, const V& value)
{
if (!key_exists(key))
{
if (map_K_V.insert( std::make_pair( key, value ) ).second)
{
return true;
}
}
return false;
}
V get_value(const K& key)
{
return map_K_V[ key ];
}
};
Template just like std::map, just more organized for other uses.
main.cpp
#include <iostream>
#include "TMAP.h"
class A;
TMAP< std::string, A* > map_cntr;
class A
{
public:
A( std::string nm )
{
name = nm;
std::cout << "A: " << name << ", object created." << std::endl;
}
~A()
{
std::cout << "A: " << name << ", object destroyed." << std::endl;
}
void printName()
{
std::cout << "A: printName - " << name << std::endl;
}
void setName( std::string nm )
{
name = nm;
}
private:
std::string name;
};
int main() {
// Setting
A* obj1 = new A( "obj1" );
map_cntr.insert( "obj1", obj1 );
obj1->printName();
A* obj2 = new A( "obj2" );
map_cntr.insert( "obj2", obj2 );
obj2->printName();
// Getting
A* obj1_cpy;
std::string obj1_name = "obj1";
if (map_cntr.key_exists(obj1_name))
{
obj1_cpy = map_cntr.get_value(obj1_name);
obj1_cpy->printName();
obj1_cpy->setName("OBJ1");
obj1_cpy->printName();
}
}
Outputs:
A: obj1, object created.
A: printName - obj1
A: obj2, object created.
A: printName - obj2
A: printName - obj1
A: printName - OBJ1
Outputs are as expected. Besides I've heard somewhere that using std::string as template parameter is not ideal just like in the above case somewhat relating to memory or pointer. Is it fair?
It is std::map<const char*, Value> which is "problematic", as it compare only pointers and not C-string content.
Using std::map<std::string, Value> is fine.
I have a template class C. I want such a template copy constructor that it will resize the data array according to the size of the other class.
The following is a simple example. It works fine. However, notice that Constructor 2 and Constructor 3 are so similar, I am wondering whether it is possible to merge them as one?
Also, if I simple remove Constructor 3, then C<int> c3( c1 ) won't call Constructor 2, but will rather call a default copy constructor added by the compiler. This will result in the memory not being allocated properly.
template<typename T>
class C
{
public:
T* val;
int size;
public:
C( int s = 0 ) {
cout << "Constructor 1" << endl;
size = s;
val = ( size ) ? new T[size] : nullptr;
}
template<class T2>
C( const C<T2>& c2 ) {
cout << "Constructor 2" << endl;
size = c2.size;
val = ( size ) ? new T[size] : nullptr;
}
C( const C<T>& c2 ) {
cout << "Constructor 3" << endl;
size = c2.size;
val = ( size ) ? new T[size] : nullptr;
}
~C() {
cout << "~C()" << endl;
delete[] val;
val = nullptr;
}
};
int main(void)
{
C<int> c1( 5 );
C<float> c2( c1 );
C<int> c3( c1 );
return 0;
}
Output of the above function:
Constructor 1
Constructor 2
Constructor 3
~C()
~C()
~C()
Replace
template<class T2>
C( const C<T2>& c2 ) {
cout << "Constructor 2" << endl;
size = c2.size;
val = ( size ) ? new T[size] : nullptr;
}
C( const C<T>& c2 ) {
cout << "Constructor 3" << endl;
size = c2.size;
val = ( size ) ? new T[size] : nullptr;
}
by
template<class T2>
C( const C<T2>& c2, int dummy) {
cout << "Constructor 2" << endl;
size = c2.size;
val = ( size ) ? new T[size] : nullptr;
}
template<class T2>
C( const C<T2>& c2 ) : C(c2, 0) {}
C( const C<T>& c2 ) : C(c2, 0) {}
Update
You can just use:
C( int s = 0 ) {
cout << "Constructor 1" << endl;
size = s;
val = ( size ) ? new T[size] : nullptr;
}
template<class T2>
C( const C<T2>& c2 ) : C(c2.size) {}
C( const C<T>& c2 ) : C(c2.size) {}
and not need the second constructor.
One would like to implement this with a C++11 delegating constructor. However due to ambiguity in if template paramaters are to the class or to the constructor, this is not possible in a straightforward manner. As a workaround one can factor out the common code into a helper function.
something like:
template<class T2>
C( const C<T2>& c2 ) {
Copy<T2>(c2);
}
C( const C<T>& c2 ) {
Copy<T>(c2);
}
private:
template<class T2>
void Copy( const C<T2>& c2 ) {
cout << "Constructor 2" << endl;
size = c2.size;
val = ( size ) ? new T[size] : nullptr;
}
I have a base class and define a operator== on it. And B is a subclass of A and I forget to define operator== on B. Then A::operator== is used on comparing B and usually this gives an unexpected results. Any good method to avoid such "forget"? I add an example to clarify my question.
class A
{
public:
bool operator==(const A& rhs) const
{
return i == rhs.i;
}
int i
};
class B : public A
{
public:
int j;
}
B b1, b2;
b1.i = 1; b1.j = 2;
b2.i = 1; b1.j = 3;
bool b = (b1 == b2); // will be true
What you could try is put A in a namespace, create operator == as a template non-member also in that namespace and let ADL take care of it.
#include <iostream>
namespace stuff {
class A
{
};
class B : public A {};
template <typename T>
bool operator == (const T &lhs, const T &rhs)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
return &lhs == &rhs; // <-- replace this with something real
}
}
struct C {};
int main()
{
stuff::A a, aa;
stuff::B b, bb;
C c, cc;
b == bb;
aa == a;
aa == cc; // error: no match for "operator==" stuff::A and C
b == a; // error: no match for "operator==" stuff::B and stuff::A
}
Edit: For your edited example where you want the equality check to compare each part of the class with the other respective corresponding part, DyP's suggestion can work. For example:
// same as before
// ...
class A
{
public:
bool is_equal(const A &rhs) const { return i == rhs.i; }
};
class B : public A
{
public:
bool is_equal(const B &rhs) const { return A::is_equal(rhs) && (j == rhs.j); }
};
template <typename T>
bool operator == (const T &lhs, const T &rhs)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
return lhs.is_equal(rhs);
}
Now comparing this again in the using code:
// ...
b.i = 1, bb.i = 1;
b.j = 1, bb.j = 42;
cout << boolalpha << (b == bb) << '\n';
b.j = 42;
cout << (b == bb) << '\n';
a.i = 2, aa.i = 3;
cout << (aa == a) << '\n';
outputs:
bool stuff::operator==(const T&, const T&) [with T = stuff::B]
false
bool stuff::operator==(const T&, const T&) [with T = stuff::B]
true
bool stuff::operator==(const T&, const T&) [with T = stuff::A]
false
Allowing implicit conversions for greatwolf's great approach is a bit tricky:
#include <type_traits>
namespace stuff
{
template<class T, class U>
bool operator== (const T &lhs, const U &rhs)
{
using namespace std;
static_assert(is_convertible<T, U>{} || is_convertible<U, T>{},
"invalid argument type");
static_assert
(
is_same<T, U>{}
|| ( not is_base_of<T, U>{} && not is_base_of<U, T>{})
, "use explicit casts to compare derived to base class types"
);
return is_equal(lhs, rhs);
}
template<class T>
bool is_equal(T const&, T const&)
{
// force compile-time failure when instantiating
static_assert(std::is_same<T, void>{},
"no free is_equal function for these argument types available");
return false;
}
class A
{
private:
int i;
friend bool is_equal(A const& lhs, A const& rhs)
{ return lhs.i == rhs.i; }
public:
A(int p_i) : i(p_i) {}
};
class B : public A
{
int j;
public:
B(int p_i, int p_j) : A(p_i), j(p_j) {}
};
class C : public A
{
private:
int j;
friend bool is_equal(C const& lhs, C const& rhs)
{
return is_equal(static_cast<A const&>(rhs),
static_cast<A const&>(lhs))
&& lhs.j == rhs.j;
}
public:
C(int p_i, int p_j) : A(p_i), j(p_j) {}
};
}
struct D
{
operator stuff::C() const
{
return stuff::C(1, 42);
}
};
#include <iostream>
int main()
{
stuff::A a(1), aa(1);
stuff::B b(1, 42), bb(1, 42);
stuff::C c(1, 42), cc(1, 42);
D d;
// commented lines invoke compilation failures
std::cout << "a == aa: " << (a == aa) << std::endl;
//std::cout << "a == b : " << (a == b ) << std::endl;
//std::cout << "b == bb: " << (b == bb) << std::endl;
//std::cout << "a == c : " << (a == c ) << std::endl;
std::cout << "c == cc: " << (c == cc) << std::endl;
std::cout << "d == c : " << (d == c ) << std::endl;
}
Why do you have equality comparison in a class hierarchy? In many cases, this indicates a problem with the design, with classes that don't properly behave like value types, but not properly like objects from a hierarchy either.
I came across one requirement where the record is stored as
Name : Employee_Id : Address
where Name and Employee_Id are supposed to be keys that is, a search function is to be provided on both Name and Employee Id.
I can think of using a map to store this structure
std::map< std:pair<std::string,std::string> , std::string >
// < < Name , Employee-Id> , Address >
but I'm not exactly sure how the search function will look like.
Boost.Multiindex
This is a Boost example
In the above example an ordered index is used but you can use also a hashed index:
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <string>
#include <iostream>
struct employee
{
int id_;
std::string name_;
std::string address_;
employee(int id,std::string name,std::string address):id_(id),name_(name),address_(address) {}
};
struct id{};
struct name{};
struct address{};
struct id_hash{};
struct name_hash{};
typedef boost::multi_index_container<
employee,
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique<boost::multi_index::tag<id>, BOOST_MULTI_INDEX_MEMBER(employee,int,id_)>,
boost::multi_index::ordered_unique<boost::multi_index::tag<name>,BOOST_MULTI_INDEX_MEMBER(employee,std::string,name_)>,
boost::multi_index::ordered_unique<boost::multi_index::tag<address>, BOOST_MULTI_INDEX_MEMBER(employee,std::string,address_)>,
boost::multi_index::hashed_unique<boost::multi_index::tag<id_hash>, BOOST_MULTI_INDEX_MEMBER(employee,int,id_)>,
boost::multi_index::hashed_unique<boost::multi_index::tag<name_hash>, BOOST_MULTI_INDEX_MEMBER(employee,std::string,name_)>
>
> employee_set;
typedef boost::multi_index::index<employee_set,id>::type employee_set_ordered_by_id_index_t;
typedef boost::multi_index::index<employee_set,name>::type employee_set_ordered_by_name_index_t;
typedef boost::multi_index::index<employee_set,name_hash>::type employee_set_hashed_by_name_index_t;
typedef boost::multi_index::index<employee_set,id>::type::const_iterator employee_set_ordered_by_id_iterator_t;
typedef boost::multi_index::index<employee_set,name>::type::const_iterator employee_set_ordered_by_name_iterator_t;
typedef boost::multi_index::index<employee_set,id_hash>::type::const_iterator employee_set_hashed_by_id_iterator_t;
typedef boost::multi_index::index<employee_set,name_hash>::type::const_iterator employee_set_hashed_by_name_iterator_t;
int main()
{
employee_set employee_set_;
employee_set_.insert(employee(1, "Employer1", "Address1"));
employee_set_.insert(employee(2, "Employer2", "Address2"));
employee_set_.insert(employee(3, "Employer3", "Address3"));
employee_set_.insert(employee(4, "Employer4", "Address4"));
// search by id using an ordered index
{
const employee_set_ordered_by_id_index_t& index_id = boost::multi_index::get<id>(employee_set_);
employee_set_ordered_by_id_iterator_t id_itr = index_id.find(2);
if (id_itr != index_id.end() ) {
const employee& tmp = *id_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by non existing id using an ordered index
{
const employee_set_ordered_by_id_index_t& index_id = boost::multi_index::get<id>(employee_set_);
employee_set_ordered_by_id_iterator_t id_itr = index_id.find(2234);
if (id_itr != index_id.end() ) {
const employee& tmp = *id_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by name using an ordered index
{
const employee_set_ordered_by_name_index_t& index_name = boost::multi_index::get<name>(employee_set_);
employee_set_ordered_by_name_iterator_t name_itr = index_name.find("Employer3");
if (name_itr != index_name.end() ) {
const employee& tmp = *name_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by name using an hashed index
{
employee_set_hashed_by_name_index_t& index_name = boost::multi_index::get<name_hash>(employee_set_);
employee_set_hashed_by_name_iterator_t name_itr = index_name.find("Employer4");
if (name_itr != index_name.end() ) {
const employee& tmp = *name_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by name using an hashed index but the name does not exists in the container
{
employee_set_hashed_by_name_index_t& index_name = boost::multi_index::get<name_hash>(employee_set_);
employee_set_hashed_by_name_iterator_t name_itr = index_name.find("Employer46545");
if (name_itr != index_name.end() ) {
const employee& tmp = *name_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
return 0;
}
If you want to use std::map, you can have two separate containers, each one having adifferent key (name, emp id) and the value should be a pointer the structure, so that you will not have multiple copies of the same data.
Example with tew keys:
#include <memory>
#include <map>
#include <iostream>
template <class KEY1,class KEY2, class OTHER >
class MultiKeyMap {
public:
struct Entry
{
KEY1 key1;
KEY2 key2;
OTHER otherVal;
Entry( const KEY1 &_key1,
const KEY2 &_key2,
const OTHER &_otherVal):
key1(_key1),key2(_key2),otherVal(_otherVal) {};
Entry() {};
};
private:
struct ExtendedEntry;
typedef std::shared_ptr<ExtendedEntry> ExtendedEntrySptr;
struct ExtendedEntry {
Entry entry;
typename std::map<KEY1,ExtendedEntrySptr>::iterator it1;
typename std::map<KEY2,ExtendedEntrySptr>::iterator it2;
ExtendedEntry() {};
ExtendedEntry(const Entry &e):entry(e) {};
};
std::map<KEY1,ExtendedEntrySptr> byKey1;
std::map<KEY2,ExtendedEntrySptr> byKey2;
public:
void del(ExtendedEntrySptr p)
{
if (p)
{
byKey1.erase(p->it1);
byKey2.erase(p->it2);
}
}
void insert(const Entry &entry) {
auto p=ExtendedEntrySptr(new ExtendedEntry(entry));
p->it1=byKey1.insert(std::make_pair(entry.key1,p)).first;
p->it2=byKey2.insert(std::make_pair(entry.key2,p)).first;
}
std::pair<Entry,bool> getByKey1(const KEY1 &key1)
{
const auto &ret=byKey1[key1];
if (ret)
return std::make_pair(ret->entry,true);
return std::make_pair(Entry(),false);
}
std::pair<Entry,bool> getByKey2(const KEY2 &key2)
{
const auto &ret=byKey2[key2];
if (ret)
return std::make_pair(ret->entry,true);
return std::make_pair(Entry(),false);
}
void deleteByKey1(const KEY1 &key1)
{
del(byKey1[key1]);
}
void deleteByKey2(const KEY2 &key2)
{
del(byKey2[key2]);
}
};
int main(int argc, const char *argv[])
{
typedef MultiKeyMap<int,std::string,int> M;
M map1;
map1.insert(M::Entry(1,"aaa",7));
map1.insert(M::Entry(2,"bbb",8));
map1.insert(M::Entry(3,"ccc",9));
map1.insert(M::Entry(7,"eee",9));
map1.insert(M::Entry(4,"ddd",9));
map1.deleteByKey1(7);
auto a=map1.getByKey1(2);
auto b=map1.getByKey2("ddd");
auto c=map1.getByKey1(7);
std::cout << "by key1=2 (should be bbb ): "<< (a.second ? a.first.key2:"Null") << std::endl;
std::cout << "by key2=ddd (should be ddd ): "<< (b.second ? b.first.key2:"Null") << std::endl;
std::cout << "by key1=7 (does not exist): "<< (c.second ? c.first.key2:"Null") << std::endl;
return 0;
}
Output:
by key1=2 (should be bbb ): bbb
by key2=ddd (should be ddd ): ddd
by key1=7 (does not exist): Null
If EmployeeID is the unique identifier, why use other keys? I would use EmployeeID as the internal key everywhere, and have other mappings from external/human readable IDs (such as Name) to it.
C++14 std::set::find non-key searches solution
This method saves you from storing the keys twice, once one the indexed object and secondly on as the key of a map as done at: https://stackoverflow.com/a/44526820/895245
This provides minimal examples of the central technique that should be easier to understand first: How to make a C++ map container where the key is part of the value?
#include <cassert>
#include <set>
#include <vector>
struct Point {
int x;
int y;
int z;
};
class PointIndexXY {
public:
void insert(Point *point) {
sx.insert(point);
sy.insert(point);
}
void erase(Point *point) {
sx.insert(point);
sy.insert(point);
}
Point* findX(int x) {
return *(this->sx.find(x));
}
Point* findY(int y) {
return *(this->sy.find(y));
}
private:
struct PointCmpX {
typedef std::true_type is_transparent;
bool operator()(const Point* lhs, int rhs) const { return lhs->x < rhs; }
bool operator()(int lhs, const Point* rhs) const { return lhs < rhs->x; }
bool operator()(const Point* lhs, const Point* rhs) const { return lhs->x < rhs->x; }
};
struct PointCmpY {
typedef std::true_type is_transparent;
bool operator()(const Point* lhs, int rhs) const { return lhs->y < rhs; }
bool operator()(int lhs, const Point* rhs) const { return lhs < rhs->y; }
bool operator()(const Point* lhs, const Point* rhs) const { return lhs->y < rhs->y; }
};
std::set<Point*, PointCmpX> sx;
std::set<Point*, PointCmpY> sy;
};
int main() {
std::vector<Point> points{
{1, -1, 1},
{2, -2, 4},
{0, 0, 0},
{3, -3, 9},
};
PointIndexXY idx;
for (auto& point : points) {
idx.insert(&point);
}
Point *p;
p = idx.findX(0);
assert(p->y == 0 && p->z == 0);
p = idx.findX(1);
assert(p->y == -1 && p->z == 1);
p = idx.findY(-2);
assert(p->x == 2 && p->z == 4);
}