Apple.h
class Apple {
public:
Apple(int);
static int typeID;
private:
int id_;
};
苹果.cpp
#include "Apple.h"
Apple::Apple(int pID) {
id_ = pID;
}
Potato.h,Potato.cpp与苹果相同
存储.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;
}
};
存储.cpp
#include "Storage.h"
int Apple::typeID;
int Potato::typeID;
Storage::Storage() {
Apple::typeID = 0;
Potato::typeID =1;
}
主.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;
}
此代码有效并输出正确大小的向量,但如果 switch 语句(在 Storage.h 内部)中的大小写行未注释,编译器 (g++) 会抛出"错误:无法在分配中将'std::vector <Potato*>* '转换为'std::vector<Apple*>* '"。无论如何,这就像编译器尝试这两种情况一样,我找不到是否有可能以及如何避免这种情况。我需要这方面的帮助,也许需要一些关于整个事情的建议(一个用于不同类型的容器的界面),我最近开始学习C++,可能我在这里尝试这样做的方式完全是一团糟。
您的代码无法编译,因为两个case
都应该编译,这是不可能的,因为两种case
的类型都不同。
这个问题的一个解决方案是使用重载而不是函数模板作为(这意味着,你的类中不需要typeID
!
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
}
并称其为:
template<typename FoodType>
void store(FoodType * object)
{
std::vector<FoodType> * basket = get_basket(static_cast<FoodType*>(0));
basket->push_back(object);
}
这里的诀窍是你有两个重载,每个重载都采用一个不同类型的参数,因此你使用 static_cast<FoodType*>(0)
来帮助编译器根据表达式的类型选择正确的重载static_cast<FoodType*>(0)
类型,可以是 Apple*
或 Potato*
类型。
@Gorpik评论中说,这两种(这个以及其他解决方案)都很丑陋,所以这是解决这个问题的另一种尝试。
将base_storage
类模板定义为:
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();
}
};
这个基类只存储一种类型的食品项目,但在问题中,我们需要存储两种类型的食物项目。因此,为了做到这一点,让我们定义从上面的类模板派生的另一个类Storage
为:
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();
}
};
请注意以下两点:
- 类
storage
现在没有任何成员数据。它只是将调用转发到基类,基类是根据模板参数的类型选择的FoodType
。 - 它私下派生自基类。所以这不是
is-a
关系。
在此处查看此解决方案的在线演示:http://ideone.com/Ykjo5
这个解决方案的美妙之处在于,如果你想让它适用于三种类型的食物,那么你只需要从三个基类派生它,如下:
class storage : private base_storage<Apple>,
private base_storage<Potato>,
private base_storage<Mango> //added line!
{
//same as before; no change at all !
};
演示 : http://ideone.com/lnMds
std::vector<Apple *>
和std::vector<Potato *>
是不同的类型。C++模板在编译时生成类定义及其所有代码的完整副本,因此生成的具体类型之间根本没有关系。
如果Apple
和Potato
共享一个公共父类,比如说Food
你可以将这两种东西存储在一个std::vector<Food *>
中,但你仍然无法返回一个std::vector<Apple *>
,而std::vector<Food *>
是需要
您的 getBasket()
方法尝试通过模板化返回类型来解决此问题,但不能,因为模板在编译时计算,而 switch 语句在运行时计算。编译器必须在程序开始运行之前确定 foodStuff
参数是什么,因此它不知道该 switch 语句的结果可能是什么。然而,无论如何它都必须做出决定,并且它从函数体中找到的第一个return
语句得出的结果已经消失了,不幸的是,第二个语句无效。
因此,您需要分别存储和检索Apple
s 和 Potato
s(可能像其他一些答案建议的那样使用模板),或者使用继承,以便您可以将它们存储在理解常见父类型的东西中。
您甚至不需要使用 typeID 字段。让编译器完成所有工作。
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_;
}
专门针对这两种情况进行getBasket
。
或者更好的是,不要以这种方式混合浸泡水平,而只需提供单独的马铃薯和苹果容器。