首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >开关状态情况下的不同类型赋值,在模板函数中

开关状态情况下的不同类型赋值,在模板函数中
EN

Stack Overflow用户
提问于 2012-06-14 22:00:42
回答 4查看 1K关注 0票数 5

Apple.h

代码语言:javascript
复制
class Apple {
public:
    Apple(int);
    static int typeID;
private:
    int id_;
};

Apple.cpp

代码语言:javascript
复制
#include "Apple.h"
Apple::Apple(int pID) {
    id_ = pID;
}

与苹果相同的Potato.cpp

Storage.h

代码语言:javascript
复制
#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

代码语言:javascript
复制
#include "Storage.h"
int Apple::typeID;
int Potato::typeID;
Storage::Storage() {
    Apple::typeID = 0;
    Potato::typeID =1;
}

main.cpp

代码语言:javascript
复制
#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内)中的case行未注释,编译器(g++)会在赋值中抛出"error: cannot convert 'std:: vector < Potato*>*‘to’std::vector< Apple*>*‘“。这就像编译器尝试了两种情况一样,我找不到它是不是可能的,以及如何避免这种情况。我需要这方面的帮助,也许还需要一些关于整个事情的建议(不同类型容器的一个接口),我最近开始学习C++,可能我在这里尝试的方式是完全混乱的。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-06-14 22:13:51

您的代码无法编译,因为两个case中的类型不同,所以两个case都应该编译,这是不可能的。

这个问题的一个解决方案是使用重载而不是函数模板as (这意味着在您的类中不需要typeID!):

代码语言:javascript
复制
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
}

并将其称为:

代码语言:javascript
复制
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 said in the comment认为(这个解决方案和其他解决方案)都很难看,所以这里是解决这个问题的另一个尝试。

base_storage类模板定义为:

代码语言:javascript
复制
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,如下所示:

代码语言:javascript
复制
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.
  • It从基类私有派生的模板参数类型选择的。所以它不是is-a relationship.

请在此处查看此解决方案的在线演示:http://ideone.com/Ykjo5

这个解决方案的美妙之处在于,如果你想让它适用于三种食物,那么你只需要从三个基类中派生出如下所示:

代码语言:javascript
复制
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

票数 2
EN

Stack Overflow用户

发布于 2012-06-14 22:20:23

您甚至不需要使用typeID字段。让编译器来做所有的工作。

代码语言:javascript
复制
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_;
} 
票数 2
EN

Stack Overflow用户

发布于 2012-06-14 22:33:22

std::vector<Apple *>std::vector<Potato *>是不同的类型。C++模板在编译时生成类定义及其所有代码的完整副本,因此生成的具体类型之间完全没有关系。

如果ApplePotato共享一个共同的父类,比如Food,您可以将这两种类型的内容都存储在std::vector<Food *>中,但您仍然无法在需要std::vector<Apple *>的地方返回std::vector<Food *>

您的getBasket()方法试图通过模板化返回类型来解决这个问题,但它不能,因为模板是在编译时计算的,而switch语句是在运行时计算的。编译器必须在程序开始运行之前决定foodStuff参数是什么,因此它不知道该switch语句的结果是什么。然而,它无论如何都要做出决定,而且它已经失去了从它在函数体中找到的第一个return语句中获得的结果,不幸的是,第二个语句无效。

因此,您需要分别存储和检索ApplePotato(可能像其他一些答案所建议的那样使用模板),或者使用继承,以便您可以将它们存储在能够理解公共父类型的内容中。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11034759

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档