std :: dynamic_pointer_cast未向下正确投射

时间:2014-08-22 20:39:30

标签: c++ templates stl casting smart-pointers

我发布了一个问题,我会在发布代码后解释:

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<Widget>。如您所见,m_widgets是一个填充了小部件的列表,这些小部件是LabelEditBoxButtonLabel和Menu派生自的基类。我试图向下转到这些类,以便我可以获取类型,并使用我的getWidget()来编辑派生对象。但是我收到了这个错误:

  

错误:&#39; class std :: shared_ptr&#39;没有名为&#39; attachToMenu&#39; |

的成员

这显然意味着它没有正确投射并且它返回了一个Widget。任何帮助都会很棒,谢谢!

2 个答案:

答案 0 :(得分:1)

“Widget :: Ptr只是一个typedef std :: shared_ptr。”

我认为它是std::shared_ptr<Widget>的typedef。如果是这样,那么dynamic_pointer_cast仅在函数体内强制转换,在它之外的类型是std::shared_ptr<Widget>(这是因为返回类型意味着向上转换):

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

此外,使用getWidget<decltype(type)>您实际上希望getWidget<T>返回std::shared_ptr<std::shared_ptr<Widget>>,这远远不是您想要实现的目标。

最终,向下转型通常表明设计不佳。我相信您可以通过在attachToMenu基类中创建Widget方法虚拟来解决问题,然后在运行时不需要RTTI。考虑一下:

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 :(得分:0)

宣布

auto type = ...

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