2012-06-14 11 views
5

Apple.hDiferentes tipos de asignación en los casos de estado del conmutador, en el interior función de plantilla

class Apple { 
public: 
    Apple(int); 
    static int typeID; 
private: 
    int id_; 
}; 

Apple.cpp

#include "Apple.h" 
Apple::Apple(int pID) { 
    id_ = pID; 
} 

Potato.h, Potato.cpp idéntica a Apple

almacenamiento. h

#pragma once 
#include "Apple.h" 
#include "Potato.h" 
#include <vector> 
class Storage { 
public: 
    Storage(); 
    template<typename foodName> void store(foodName * object){ 
     (*getBasket<foodName>()).push_back(object); 
    }; 
    template<typename foodName> int countSize(){ 
     return (*getBasket<foodName>()).size(); 
    }; 

private: 
    std::vector<Apple*> applebasket_; 
    std::vector<Potato*> potatobasket_; 
    template <typename foodName> std::vector<foodName*> * getBasket(){ 
     std::vector<foodName*> * result; 
     switch(foodName::typeID){ 
      case 0: 
       result = &applebasket_; 
       break; 
      case 1: 
       //result = &potatobasket_; 
       break; 
     } 
     return result; 
    } 
}; 

Storage.cpp

#include "Storage.h" 
int Apple::typeID; 
int Potato::typeID; 
Storage::Storage() { 
    Apple::typeID = 0; 
    Potato::typeID =1; 
} 

main.cpp

#include "Storage.h" 
#include <iostream> 
int main() { 
    Apple* apple; 
    Potato* potato; 
    Storage storage; 
    int i; 
    for(i = 0;i < 7;i++){ 
     apple = new Apple(i); 
     storage.store<Apple>(apple); 
    }  
    std::cout<<storage.countSize<Apple>(); 
    return 0; 
} 

Este código funciona y el tamaño de las salidas derecha del vector, pero si la línea en caso de sentencia switch (dentro Storage.h) es sin comentar, compilador (g ++) lanza " error: no se puede convertir 'std :: vector < Potato *> *' a 'std :: vector < Apple *> *' en la asignación ". Es como compilar probando ambos casos de todos modos, y no puedo encontrarlo y cómo evitarlo. Necesito ayuda con esto y tal vez algunos consejos sobre un todo (una interfaz para contenedores de diferentes tipos), he comenzado a aprender C++ recientemente y probablemente la forma en que trato de hacerlo aquí es un desastre total.

+2

Usted ** ciertamente ** no debe establecer el 'typeID' en el constructor' Storage'. Pero +1 para una pregunta interesante. –

Respuesta

2

Su código no se compila porque ambos case deben compilar lo que no es posible, ya que el tipo es diferente en ambos case.

Una solución a este problema es utilizar una sobrecarga en lugar de plantilla de función como (lo que significa, no hay necesidad de typeID en su clase!):

std::vector<Apple*> * get_basket(Apple *) 
{ 
    return &applebasket_; //return pointer to the apple basket 
} 

std::vector<Potato*> * get_basket(Potato *) 
{ 
    return &potatobasket_; //return pointer to the potate basket 
} 

y lo llaman como:

template<typename FoodType> 
void store(FoodType * object) 
{ 
    std::vector<FoodType> * basket = get_basket(static_cast<FoodType*>(0)); 
    basket->push_back(object); 
} 

el truco aquí es que usted tiene dos sobrecargas, cada uno toma un argumento de diferente tipo, y por lo que utilizar static_cast<FoodType*>(0) para ayudar al compilador para recoger la sobrecarga correcta en función del tipo de la expresión static_cast<FoodType*>(0) que sería del tipo Apple* o Potato*.


@Gorpik said in the comment que ambos (esto, así como otra solución) son feos, así que aquí es otro intento de resolver este problema.

definir una plantilla base_storage clase como:

template<typename FoodType> 
class base_storage 
{ 
    std::vector<FoodType*> m_storage; 
    public: 
     void store(FoodType *foodItem) 
     { 
      m_storage.push_back(foodItem); 
     } 
     size_t count() const 
     { 
      return m_storage.size(); 
     } 
}; 

Esta base de tiendas de la categoría alimentos de una única tipo, pero en la pregunta, tenemos que almacenar los alimentos de dos tipos.Así que con el fin de hacer eso, vamos a definir otra clase Storage derivado de la plantilla de clase anterior como:

class storage : private base_storage<Apple>, private base_storage<Potato> 
{ 
    public: 
     template<typename FoodType> 
     void store(FoodType * foodItem) 
     { 
      base_storage<FoodType>::store(foodItem); 
     } 
     template<typename FoodType> 
     size_t count() const 
     { 
      return base_storage<FoodType>::count(); 
     } 
}; 

Nota dos puntos aquí:

  • La clase storage no tiene ningún datos de los miembros ahora. Simplemente reenvía la llamada a la clase base que se elige en función del tipo de argumento de plantilla FoodType.
  • Deriva en privado de las clases base. Entonces, no es una relación is-a.

Ver la demostración en línea de esta solución aquí: http://ideone.com/Ykjo5

La belleza de esta solución es que si quieres hacer que funcione para tres tipos de alimentos, entonces todo lo que necesita para derivarla de tres clases de base como:

class storage : private base_storage<Apple>, 
       private base_storage<Potato>, 
       private base_storage<Mango> //added line! 
{ 

    //same as before; no change at all ! 

}; 

demostración: http://ideone.com/lnMds

+1

Esto funciona, ¡Gracias! –

+0

@ Человек Ящерица: Vea la solución alternativa. – Nawaz

+1

Creo que esta es una manera elegante. Lo usaré –

0

Especializar getBasket para los dos casos.

O mejor, no mezcle los niveles de abstracción de esta manera, y solo proporcione contenedores de papa y manzana separados.

2

Ni siquiera necesita usar el campo tipoID. Deje que el compilador haga todo el trabajo.

class Storage { 
public: 
    Storage(); 
    template<typename foodName> void store(foodName * object){ 
     (*getBasket<foodName>()).push_back(object); 
    }; 
    template<typename foodName> int countSize(){ 
     return (*getBasket<foodName>()).size(); 
    }; 

private: 
    std::vector<Apple*> applebasket_; 
    std::vector<Potato*> potatobasket_; 

    template <typename foodName> std::vector<foodName*> * getBasket(); 
}; 


template <> std::vector<Apple*> * Storage::getBasket() 
{ 
    return &applebasket_; 
} 
template <> std::vector<Potato*> * Storage::getBasket() 
{ 
    return &potatobasket_; 
} 
+0

por favor elimine el; al final de las especializaciones getBacket(). – PermanentGuest

+0

gracias. los eliminé – inkooboo

+0

[La especialización de la plantilla de función NO es algo bueno.] (http://www.gotw.ca/publications/mill17.htm) – Nawaz

2

std::vector<Apple *> y std::vector<Potato *> son diferentes tipos. Las plantillas de C++ producen una copia completa de la definición de clase y todo su código en tiempo de compilación, por lo que no existe ninguna relación entre los tipos concretos resultantes.

Si Apple y Potato compartieron una clase padre común, dicen Food podría almacenar ambos tipos de cosas en un std::vector<Food *>, pero aún así no sería capaz de devolver un std::vector<Apple *> donde se esperaba un std::vector<Food *>.

Su método getBasket() intenta evitar esto templating el tipo de retorno, pero no puede porque las plantillas se evalúan en tiempo de compilación mientras que la sentencia switch se evalúa en tiempo de ejecución . El compilador tiene que decidir qué es el parámetro foodStuff antes de que el programa comience a ejecutarse, por lo que no tiene idea de cuál podría ser el resultado de esa instrucción de conmutación. Sin embargo, tiene que tomar una decisión de todos modos, y se ha ido para el resultado que puede derivar de la primera declaración return que encuentra en el cuerpo de la función, con la desafortunada consecuencia de que la segunda no es válida.

Por lo tanto, necesita almacenar y recuperar Apple sy Potato s por separado (posiblemente usando plantillas como algunas de las otras respuestas han sugerido), o usar herencia para que pueda almacenarlas en algo que comprenda un tipo de elemento primario común .

Cuestiones relacionadas