【问题标题】:Casting to undeclared type转换为未声明的类型
【发布时间】:2016-01-19 08:35:18
【问题描述】:

这个想法与GetComponent() in Unity 的通用版本相同。但我目前遇到以下模板问题:

template<class T> std::shared_ptr<T> MyClass::GetMyComponent()
{
    for (int i = 0; i < _baseTypeList.size(); i++)
    {
        auto base = _baseTypeList[i];
        T* check = dynamic_cast<T*>(base.get());
        if (check)
        {
            return std::static_pointer_cast<T>(base);
        }
    }
    return std::shared_ptr<T>(nullptr);
}

其中_baseTypeListstd::vector&lt;std::shared_pntr{MyBaseType}&gt; 类型。

在这个函数中,我正在遍历一个组件列表,以查找是否有一个与我要求的类型匹配。如果有,则将转换为该类型的组件返回。否则返回nullptr

但是,当我从外部代码调用此函数时,出现以下错误:

error C2680: 'MyType*' : invalid target type for dynamic_cast

其中MyType 是派生自组件的某个类。

当我将 #include "MyType.h" 放在标头中时,它编译得很好,但没有它会出现此错误并且无法编译。

这意味着如果不修改这个模板类所在的头文件,我就不能在其他类中使用它,这对我来说是个问题。

有没有一种方法可以实现类似的结果,而无需 #include 我在模板中传递的每个类型的标头?

[编辑] 为清楚起见,考虑一个使用我的库的人,他创建了一个类型 “Foo : MyBaseType”,其中 MyBaseType 有一个虚拟方法“Update”,每帧都会调用它。 MyBaseType 类的任何实例(包括 Foo)都将由该库管理,并在每一帧都调用更新。 因此,该库具有大量“MyBaseType”对象。但不知道它们的实际类型,只是它们派生自“MyBaseType”,因此它可以对它们调用 Update()。

如果我需要特定类型,库需要能够在此列表中搜索并返回它。 我希望这个“搜索”发生在库本身中,所以我不必公开列表,并为从“MyBaseType”派生的每种类型编写一个新的“搜索”方法

[最终] 原来我搞砸了项目中的包含顺序。 我试图做的一个最小的例子是:

#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <memory>
#include "vector"

class MyBaseClass
{
    virtual void Update(){};
};

class MyLibrary
{
public:
    template<class T> std::shared_ptr<T> GetComponent();
    std::vector<std::shared_ptr<MyBaseClass>> list;
};

template<class T> std::shared_ptr<T> MyLibrary::GetComponent()
{
    static_assert(std::is_base_of<MyBaseClass, T>::value, "T1 is no subclass of ModelComponent");
    for (unsigned int i = 0; i < list.size(); i++)
    {
        auto comp = list[i];
        T* check = dynamic_cast<T*>(comp.get());
        if (check)
        {
            return std::static_pointer_cast<T>(comp);
        }
    }
    return std::shared_ptr<T>(nullptr);
}

class MyClass : public MyBaseClass
{
    void Update() override;
};

void MyClass::Update()
{

}

int _tmain(int argc, _TCHAR* argv[])
{
    MyLibrary lib;
    lib.list.push_back(std::make_shared<MyClass>());
    auto var = lib.GetComponent<MyClass>();
    std::cout << (var ? "var is object" : "var is not") << std::endl;

    while (true)
    {

    }
    return 0;
}

按预期工作。

主要问题是编译器在“GetMyComponent”函数中给出了错误,所以我发现它的用法可以按照建议完成所有操作。

但事实证明,第二种用法在调用它之前没有“MyClass”的定义(但没有给出错误,因为它是在其头文件中前向声明的)。

【问题讨论】:

  • shared_ptr&lt;MyType&gt; 对于没有MyType 定义的调用者有什么用?无论如何,你不能 dynamic_cast 或者,就此而言,static_cast MyBaseType*MyType* 不知道两者之间的关系。您似乎声称 MyType 派生自 MyBaseType - 但是如果不是通过查看 "MyType.h" 的内容,编译器应该如何知道这一点?
  • 调用者确实有“MyType”的定义。我正在制作的一个库只是公开了这个功能。这个库只知道“MyBaseType”,其中“MyType”是一些用户定义的类,它派生自“MyBaseType”。虽然如果这确实不可能,那将是不幸的
  • 您不需要在定义MyClass::GetMyComponent 的标头中使用#include "MyType.h"。您确实需要将它包含在调用MyClass::GetMyComponent&lt;MyType&gt;() 的源文件中。这真的是太多要求了吗? Live example

标签: templates c++11 std


【解决方案1】:

您不需要将可能的T 类型定义包含在您的标头中。您确实需要在展开模板的翻译单元中定义的相关文件:

// client.cpp
#include <myclass.h>
#include <foo.h>  // defines class Foo

void f(MyClass *p)
{
    auto c = p->GetMyComponent<Foo>();
    c->foobar();
}

【讨论】:

  • 是的,这正是我正在做的。 auto myvar= MyClass*-&gt;GetMyComponent&lt;MyType&gt;(); 我的标题中没有包含任何类型,只是我发布的函数。然而,编译器告诉我必须将“MyType”添加到标题中,否则它不会编译
  • @Brian:很难说清楚你在做什么。您能否编辑您的问题,使示例为Minimal, Complete, and Verifiable?我不相信你有相当于我上面的#include &lt;foo.h&gt;; MCVE 可以帮助我重现您的问题并给出更好的答案。
  • 哦,包括你的编译器的错误信息。我怀疑它告诉你添加一个包含到标题,只是使用类型的位置。
猜你喜欢
  • 2018-10-01
  • 2021-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多