关于c ++:std :: dynamic_pointer_cast无法正确向下转换

std::dynamic_pointer_cast isn't casting correctly downwards

发布代码后遇到一个问题,我将予以解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
template <class T>
std::shared_ptr< T >
getWidget(const std::string& id) {
  auto iter = findObject(id);

  if (iter != m_widgets.end()) return std::dynamic_pointer_cast< T >(*iter);

  return nullptr;
}

const Widget::Ptr
getType(const std::string& id) {
  auto iter = findObject(id);

  if (iter != m_widgets.end()) {
    if ((*iter)->getWidgetType() =="Label")
      return std::dynamic_pointer_cast<Label>(*iter);
    else if ((*iter)->getWidgetType() =="Editbox")
      return std::dynamic_pointer_cast<EditBox>(*iter);
    else if ((*iter)->getWidgetType() =="ButtonLabel")
      return std::dynamic_pointer_cast<ButtonLabel>(*iter);
    else if ((*iter)->getWidgetType() =="Menu")
      return std::dynamic_pointer_cast<Menu>(*iter);
  }
}

auto type = SceneManager::getCurrentScene().m_gui.getType(widgetId);

SceneManager::getCurrentScene().m_gui.getWidget<decltype(type)>(widgetId)->attachToMenu(getId());

Widget::Ptr只是一个typedef std::shared_ptr。 如您所见,m_widgets是一个由小部件填充的列表,这些小部件是LabelEditBoxButtonLabel和Menu的基类。 我试图向下转换为这些类,以便获得类型,并使用我的getWidget()编辑派生对象。 我收到此错误消息:

error: 'class std::shared_ptr' has no member named 'attachToMenu'|

显然,这意味着它无法正确投射并且正在返回Widget。 任何帮助将是巨大的,谢谢!


" Widget :: Ptr只是typedef std :: shared_ptr。"

我假设它是std::shared_ptr的typedef。 如果是这样,则dynamic_pointer_cast仅在函数主体内向下转换,其类型之外为std::shared_ptr(这是因为返回类型暗示是向上转换):

1
2
auto type = SceneManager::getCurrentScene().m_gui.getType(widgetId);
// type is std::shared_ptr<Widget>, that is, a pointer to base type

此外,使用getWidget时,您实际上希望getWidget< T >返回std::shared_ptr>,这与您要实现的目标相去甚远。

最终,垂头丧气通常表示设计不良。 我相信可以通过在Widget基类中将attachToMenu方法虚拟化来解决您的问题,然后在运行时不需要RTTI。 考虑一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Widget
{
public:
    virtual ~Widget() = default;
    virtual void attachToMenu(const std::string& id) = 0;
};

class EditBox : public Widget
{
public:
    virtual void attachToMenu(const std::string& id) override
    {
        // do the stuff
    }
};

std::shared_ptr<Widget> widget = std::make_shared<EditBox>();
widget->attachToMenu(getId()); // calls EditBox::attachToMenu

当你声明

1
auto type = ...

type的类型将始终为Widget::Ptr,因为这是函数返回的类型。 所有向下转换都在函数内发生,因此无关紧要。 然后,当您使用decltype(type)调用getWidget时,将使用静态类型,因此再次使用Widget::Ptr。 您需要重新设计。