C++返回多个类型作为引用

C++ Returning Multiple Types as Reference

本文关键字:引用 类型 返回 C++      更新时间:2023-10-16

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

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->GetObject 中不断下划线 ->,并告诉我模板方法调用没有重载方法。 具体来说,它指出"没有函数模板'ObjManager::GetObject'的实例与参数列表匹配,参数类型是(int,ObjectTypes)",即使我正在传递整数和ObjectTypes::到函数的参数列表中。 我已经四处寻找答案,但无法找到类似的情况来借鉴经验。

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

正如@tobi303所评论的那样,您绝对应该在 GetObject 类中使用模板参数 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 步:将枚举转换为几个空结构:

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

第 2 步:不要使用 else if s,而是使用函数重载:

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());

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

根本原因

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

在通往解决方案的路上

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

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

main()

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

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

但这并不能解决您的问题。 此类型推导是在编译时进行的,因此函数的所有可能返回都应返回相同的类型(或可以转换为它的内容)。

您的代码并非如此,这将导致其他编译错误(请参阅联机失败)。

解决方案

唯一可行的解决方案是确定要返回的共同点。 使函数成为返回IObj&的非模板函数:

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

然后,您还应该将返回对象作为多态对象进行管理。 由于返回是通过引用的,这很好(即不会发生切片)。返回的引用将真正引用返回的对象,无论其派生类型如何。但是你必须重新设计你的调用代码以实现多态性:

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

在线演示

但这有点笨拙,因为您在枚举中指示预期的类型,但随后以对您无法轻松处理的父级的引用结束。

结论

当您在函数中指示预期返回类型时,您最好选择优素福的 rexcellent 答案中的解决方案,但应用虚拟参数的技术进行类型推导。

好的,

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

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

让我知道您对这是否可以接受或是否可以改进的意见! :)