Store a reference to an objects member variable with a different class
I am trying to create a Container class where I can retrieve an object from the container by using that objects member variable as its identifier. But I get a compile error because I am trying to store a pointer(?)/reference to the objects member variable
template <typename Object>
class Container
{
private:
template <typename dataType>
dataType Object::* memberVariable; // error here "data member 'memberVariable' cannot be a member template"
template <typename dataType>
std::map <dataType, Object*> instanceVarMap; // what would be more efficient; a Map or unordered_map? I heard that
std::map <unsigned int, Object*> instanceIntMap; // ...unordered_maps use more mem开发者_如何学JAVAory & Maps are better when integers are the keys
public;
template <typename dataType>
Collection( dataType Object::*nMemberVariable )
{
memberVariable = nMemberVariable;
}
template <typename dataType>
Object* operator[] ( dataType nParam )
{
// do I need to check whether the element already exists or does
// stl already do this for me?
if ( instanceVarMap.find(nParam) == instanceVarMap.end() )
{
return NULL;
}
return instanceVarMap[ nParam ];
}
Object* operator[] ( unsigned int nParam )
{
if ( instanceIntMap.find(nParam) == instanceIntMap.end() )
{
return NULL;
}
return instanceIntMap[ nParam ];
}
void store( Object* o )
{
if ( o==NULL || instanceMap.contains(o->memeberVariable) != instanceMap.end() ) { return; }
instanceIntMap.insert( o->ID, o );
instanceVarMap.insert( o->memberVariable, o ); // is this the correct way I get the objects member variable? o->memberVariable
}
};
// I am doing this so I can use the class like so
struct FoodItem
{
unsigned int ID;
string name;
double price;
};
Collection <FoodItem*> foodCol( &FoodItem::name );
// after storing some FoodItems in foodCol, I can retreive a FoodItem either
// by their ID or their name
FoodItem* f = foodCol["coffee"]; // find FoodItem by their member variable 'name'
FoodItem* g = foodCol[1]; // find FoodItem by their ID var
Declaring a template data member is not allowed in C++ (not to confuse with template syntax used while defining a static member). The best way to achieve is,
template <typename Object, typename dataType> // <-- Add dataType here
class Container
{
private:
dataType Object::* memberVariable; // use 'dataType' simply
// ...
};
template <typename dataType>
dataType Object::* memberVariable; // error
Declaring a tempate data member? That is not allowed by C++. And I cannot you suggest any alternative, because I don't actually understand what exactly you're trying to do.
Why don't you first try using containers provided by the Standard Library? Have you seen std::vector
, std::list
, std::map
etc, along with generic functions from <algorithm>
?
In the following there are two variations of what you are asking for: the first in which the member is a template parameter, and the second in which the member is instead passed as argument when building the map...
#include <map>
#include <string>
#include <iostream>
template<typename Object, typename MemberType, MemberType Object::*member>
struct MemberMap
{
std::map<MemberType, Object *> mmap;
Object * operator[](const MemberType& mv) const
{
typename std::map<MemberType, Object *>::const_iterator i = mmap.find(mv);
return i == mmap.end() ? NULL : i->second;
}
void store(Object *o)
{
if (o && mmap.find(o->*member) == mmap.end())
mmap[o->*member] = o;
}
};
template<typename Object, typename MemberType>
struct MemberMapByInst
{
MemberType Object::*member;
std::map<MemberType, Object *> mmap;
MemberMapByInst(MemberType Object::*member) : member(member)
{
}
Object * operator[](const MemberType& mv) const
{
typename std::map<MemberType, Object *>::const_iterator i = mmap.find(mv);
return i == mmap.end() ? NULL : i->second;
}
void store(Object *o)
{
if (o && mmap.find(o->*member) == mmap.end())
mmap[o->*member] = o;
}
};
struct Foo
{
std::string name;
Foo(const std::string& name) : name(name)
{
}
};
int main()
{
Foo foo1("This is a test");
Foo foo2("This is another test");
MemberMap<Foo, std::string, &Foo::name> namemap;
namemap.store(&foo1);
namemap.store(&foo2);
MemberMapByInst<Foo, std::string> namemap2(&Foo::name);
namemap2.store(&foo1);
namemap2.store(&foo2);
std::cout << (namemap["This is a test"] != NULL) << std::endl;
std::cout << (namemap["What about this?"] != NULL) << std::endl;
std::cout << (namemap2["This is a test"] != NULL) << std::endl;
std::cout << (namemap2["What about this?"] != NULL) << std::endl;
return 0;
}
Basically you need to move at least the member type out as a template parameter because that is needed to be able to generate the C++ code of the map. You can decide at runtime what is the member you want to use as key (second version), but its type must be fixed at compile time.
If instead even the actual member you want to use as key is known a compile time then the member pointer can be factored out as a template parameter (first version), generating more efficient code (however creating a new class for every different member - thus increasing compiled code size).
you can get away by declaring a member type like this.
struct MyObject
{
int Variable;
typedef int MyObject::* ObjectVariablePtrType;
};
template<typename Object>
class Container
{
typename Object::ObjectVariablePtrType memberVariable;
};
精彩评论