2011-05-14 8 views
6

Estoy tratando de crear una clase de contenedor en el que puedo recuperar un objeto del contenedor mediante el uso de que los objetos variable miembro como su identificador. pero me da un error de compilación porque estoy tratando de almacenar un puntero (?)/Referencia a la variable miembro de objetostienda una referencia a una variable miembro de objetos con una clase diferente

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 memory & 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 
+0

¿Qué es esto: 'Colección foodCol (& FoodItem :: name);' supposed a mean? Dirección de qué exactamente? –

+0

Supongo que te refieres a lo que significa & FoodItem :: name, me refiero a la estructura del nombre de variable de miembro de FoodItem. Así que estoy creando una Colección y quiero poder acceder a sus elementos de FoodItem usando sus nombres como claves (identificadores) – user593747

Respuesta

1

Declarar un miembro de datos de la plantilla no está permitido en C++ (no confundir con la sintaxis de plantilla utilizada mientras se define un miembro estático). La mejor manera de lograr es,

template <typename Object, typename dataType> // <-- Add dataType here 
class Container 
{ 
    private: 
     dataType Object::* memberVariable; // use 'dataType' simply 
// ... 
}; 
0
template <typename dataType> 
dataType Object::* memberVariable; // error 

Declaración de un miembro de datostempate? Eso no está permitido por C++. Y no puedo sugerir ninguna alternativa, porque realmente no entiendo exactamente lo que estás tratando de hacer.

¿Por qué no intente usar primero los contenedores proporcionados por la Biblioteca Estándar? ¿Usted ha visto std::vector, std::list, std::map etc, junto con funciones genéricas de <algorithm>?

+0

¿qué? Sé que puedo hacer esto int Object :: * memberVariable No conoceré el tipo de datos de memberVariable en tiempo de compilación, ¿hay algo que pueda hacer? – user593747

+0

@ user593747: si no conoce el tipo de datos del miembro, en la hora de COMPILAR, entonces no puede hacer lo que desea hacer. C++ es un lenguaje estáticamente tipado, es decir, el tipo de cada variable, debe conocerse en tiempo de compilación. – Nawaz

+0

puede ... siempre que al menos el miembro ** tipo ** sea conocido en tiempo de compilación. Ver mi respuesta (segundo caso). – 6502

0

en los siguientes Hay dos variaciones de lo que usted está pidiendo: la primera en la que el miembro es un parámetro de plantilla, y el segundo en el que el miembro en su lugar se pasa como argumento al construir el mapa ...

#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; 
} 

Básicamente tiene que mover al menos el miembro de tipo como un parámetro de plantilla debido a que se necesita para ser capaz de generar el código C++ del mapa. Puede decidir en tiempo de ejecución cuál es el miembro que desea usar como clave (segunda versión), pero su tipo debe ser reparado en tiempo de compilación.

Si, en cambio, se conoce un tiempo de compilación incluso el miembro real que desea utilizar como clave, el puntero miembro se puede descartar como un parámetro de plantilla (primera versión), generando un código más eficiente (sin embargo, creando una nueva clase para cada miembro diferente - aumentando así el tamaño del código compilado).

+0

¿hay alguna manera de almacenar punteros a los objetos con este método? Entonces puedo hacer este MemberMap persistantObjectMap; – user593747

0

que puede salirse declarando un tipo de miembro como este.

struct MyObject 
{ 
int Variable; 
typedef int MyObject::* ObjectVariablePtrType; 
}; 

template<typename Object> 
class Container 
{ 
    typename Object::ObjectVariablePtrType memberVariable; 
}; 
Cuestiones relacionadas