C ++返回多个类型作为参考

时间:2016-07-10 19:48:50

标签: c++ templates reference

好的,我正在尝试设置一个模板方法,该方法根据参数请求返回未确定类型的引用。一切看起来都很好,但它一直告诉我,当我调用它时,没有提供模板方法的重载方法。代码看起来像这样:

class IObj {
    public:
    int id;
}
class ObjOne : public IObj {}
class ObjTwo : public IObj {}
class ObjThree : public IObj {}

enum ObjectTypes {
    O1Type,
    O2Type,
    O3Type
}

class ObjManager {
    public:
    std::vector< std::unique_ptr<ObjOne> > O1Holder;
    std::vector< std::unique_ptr<ObjTwo> > O2Holder;
    std::vector< std::unique_ptr<ObjThree> > O3Holder;

    ObjManager() {}

    template <class T>
    T& GetObject(int oID, ObjectTypes oType) {
        if(oType == ObjectTypes::O1Type) {
            for(int i = 0; i < O1Holder.size(); i++) {
                if(O1Holder[i]->id == oID) {
                    return *O1Holder[i];
                }
            }
        }
        else if(oType == ObjectTypes::O2Type) {
            for(int i = 0; i < O2Holder.size(); i++) {
                if(O2Holder[i]->id == oID) {
                    return *O2Holder[i];
                }
            }
        }
        else if(oType == ObjectTypes::O3Type) {
            for(int i = 0; i < O3Holder.size(); i++) {
                if(O3Holder[i]->id == oID) {
                    return *O3Holder[i];
                }
            }
        }
    }
}

int main() {
    std::unique_ptr<ObjManager> oManager(new ObjManager());

    ObjOne& a = oManager->GetObject(0, ObjectTypes::O1Type);
    return 0;
}

一切正常,我可以创建一个方法,如果我返回它们的特定类型,则返回对存储在向量中的对象的引用,但我正在尝试减少使许多函数返回每个不同类型的冗余。所以我想创建一个模板化方法,根据我请求的类型返回一个对象类型。

它没有给我任何错误它只是在表达式 oManager-&gt; GetObject 中强调 - &gt; ,并告诉我没有重载的方法模板方法调用。具体来说它声明“没有函数模板的实例'ObjManager :: GetObject'匹配参数列表,参数类型是(int,ObjectTypes)”,即使我将一个整数和ObjectTypes ::传递给函数的参数列表。我已经到处寻找答案,但未能找到类似的情况来吸取经验。

编辑:很抱歉应该指出这是一个庞大的矢量列表的前身,为了简单起见,我只放了其中的3个。这就是为什么我试图创建一个可以处理不同类型返回的单个函数,这样我就不必为我创建的每个向量创建一个返回函数。返回对指定类型的引用的目的是因为每个派生类型都将具有不在基类中的唯一数据,所以我拉动对象进行编辑。

5 个答案:

答案 0 :(得分:2)

在@ tobi303评论中,您应该在GetObject类中使用模板Parameter T.然后你实际上会避免重复自己,因为编译器会为你重复3次生成代码

template <class T>
    T& GetObject(int oID) {
            for(int i = 0; i < OHolder<T>.size(); i++) {
                if(OHolder<T>[i]->id == oID) {
                    return *OHolder<T>[i];
                }
        }

虽然您也必须定义OHolder模板功能。

答案 1 :(得分:0)

无法根据运行时信息(例如参数)更改函数的返回类型,因为编译器显然不知道它们。

如果你总是在编译时知道你要选择哪种对象类型,你可以使用一个技巧:

第1步:将你的枚举变成几个空结构:

struct O1Type {};
struct O2Type {};
struct O3Type {};

步骤2:使用函数重载:

,而不是使用else if
ObjOne& GetObject(int oID, O1Type) {/* TODO*/}
ObjTwo& GetObject(int oID, O2Type) {/* TODO*/}
ObjThree& GetObject(int oID, O3Type) {/* TODO*/}

您现在可以使用

ObjOne& a = oManager->GetObject(0, O1Type());

(或者更好auto& a = oManager->GetObject(0, O1Type());

答案 2 :(得分:0)

根本原因

模板参数类型推导不能仅基于函数的返回类型。

在寻求解决方案的途中

因此,您可以添加一个虚函数参数来传输类型信息:

template <class T>
T& GetObject(int oID, ObjectTypes oType, T&x) {
    ...
} 

main()

ObjOne& a = oManager->GetObject(0, ObjectTypes::O1Type, a);

然后可以推导出模板类型。

但这不会解决你的问题。这种类型推导是在编译时,因此函数的所有可能返回应该返回相同的类型(或可以转换为它的东西)。

这不是您的代码的情况,这将导致其他编译错误(请参阅online failure)。

解决方案

唯一可行的解​​决方案是确定要返回的公分母。使函数成为非模板函数,返回IObj&

IObj& GetObject(int oID, ObjectTypes oType) {
...
} 

然后,您应该将返回对象作为多态性obhect进行管理。由于返回是参考,这很好(即没有发生切片)。返回的引用实际上将引用返回的对象,无论其派生类型是什么。但是您必须重新设计多态性的调用代码:

IObj& a = oManager->GetObject(0, ObjectTypes::O1Type);

Online demo

但是这有点笨拙,因为你在枚举中指出了期望的类型,但最后引用了一个你不能轻易处理的父类。

<强>结论

当您在函数中指出预期的返回类型时,您最好在Yussuf的优秀答案中找到解决方案,但应用伪参数的技术进行类型推导。

答案 3 :(得分:0)

您似乎正在尝试使用运行时多态性和编译时(模板)多态性。它没有这种方式。您无法从相同方法返回多种类型。 您可能想要做的是定义一个像@yussuf描述的方法,或者完全开始使用运行时多态 - 在这种情况下,您不需要三个容器,并且该类型成为对象ID的一部分。 我同意@ yussuf的方法。只要这样做,它可能会解决您的问题。 我还建议使用哈希/地图而不是执行线性搜索,但这是一个不同的故事......

答案 4 :(得分:0)

好的,经过大量研究后,我确定了实现这一目标的最佳方法是创建一个自定义容器类,如下所示:

#include <vector>
#include <memory>

class Base {
    public:
    int ID;

    Base(int id) { ID = id; }
}

class A : public Base {
    public:
    int X;

    A(int id) : Base(id) {}
}

class B : public Base {
    public:
    int Y;

    B(int id) : Base(id) {}
}

template <class T>
class MyContainer {
    private:
    std::vector<std::unique_ptr<T>> internalContainer;

    public:
    MyContainer() {}
    ~MyContainer() {}

    void CreateItem(int id) {
        std::unique_ptr<T> newItem(new T(id));
        internalContainer.push_back(std::move(newItem));
    }

    T& GetItem(int id) {
        for(std::vector<std::unique_ptr<T>>::iterator it = internalContainer.begin(); it!= internalContainer.end(); ++it) {
            if((*it)->ID == id) {
                return **it;
            }
        }
    }
}

int main() {
    MyContainer<A> AList;
    MyContainer<B> BList;

    AList.CreateItem(0);
    BList.CreateItem(0);

    A& AOne = AList.GetItem(0);
    B& BOne = BList.GetItem(0);

    AOne.X = 10;
    BOne.Y = 20;

    std::cout << std::to_string(AOne.X) << "\n";
    std::cout << std::to_string(BOne.Y) << "\n";
}

如果可以接受或者可以改进,请告诉我您的意见! :)