qobject_cast的变量模板通过parent()

时间:2016-02-29 13:14:17

标签: c++ qt templates c++11 variadic-templates

大师和模板专家,我需要你的帮助......

我目前正在寻找检查QObject的父层次结构的解决方案。我有一个自定义QDialog,具有以下层次结构(父对子):

QDockWidget
> CustomDockArea
  > QMainWindow
    > QDockWidget
      > CustomDialog

在CustomDialog类中,我想检查对话框的层次结构是否与此示例匹配,因此我检查了这是否可以通过可变参数模板实现,例如:

assert(qobject_cast_parent<QDockWidget*, QMainWindow*, CustomDockArea*, QDockWidget*>(this));

我想出了类似的东西:

template <class T, class... Ts>
inline T qobject_cast_parent(QObject* root)
{
    if (root)
    {
        if (sizeof...(Ts) == 0)
        {
            return qobject_cast<T>(root->parent());
        }
        else
        {
            return qobject_cast_parent<Ts...>(root->parent());
        }
    }
    else
    {
        return nullptr;
    }
}

然而,有一些问题:在我们的示例QDockWidget *中,我需要参数包的最后一个参数作为函数的返回类型。我可以将第一个参数作为返回类型,但这会使模板调用有点麻烦。但是,即使这样可以解决,我认为参数包的“展开”方式仍然存在问题,现在我有点不确定我的模板方法是否对原始问题是可行的。也许你可以给我一些提示。在此先感谢!!!

2 个答案:

答案 0 :(得分:2)

使用c ++ 14,您可以简单地使用auto作为返回类型:

template <class T>
T* qobject_cast_parent(QObject* root)
{
    return root
        ? qobject_cast<T*>(root->parent())
        : nullptr;
}

template <class T, class T2, class... Ts>
auto qobject_cast_parent(QObject* root)
//-> typename Last<T2, Ts...>::type /* In c++11, you have to create this traits */
{
    return root
        ? qobject_cast_parent<T2, Ts...>(qobject_cast<T*>(root->parent()))
        : nullptr;
}

答案 1 :(得分:1)

由于我没有完整的代码,我只能确认以下编译,但无法测试。我相信你可以为我测试一下,如果这不起作用或者我误解了你的问题,请告诉我。

#include <QtCore/QObject>
#include <cassert>
#include <type_traits>

// This ends the recursion with the actual qobject_cast.
template <class T, class U>
inline U *qobject_cast_parent(T* root)
{
  // Make sure everything's a QObject, clear message if not.
  static_assert(std::is_base_of<QObject, T>::value, "Object must be a QObject");

  if (root)
  {
    return qobject_cast<U *>(root->parent());
  }
  else
  {
    return nullptr;
  }
}

template <class T, class U, class... Us>
inline U *qobject_cast_parent(T* root)
{
  // Make sure everything's a QObject, clear message if not.
  static_assert(std::is_base_of<QObject, T>::value, "Object must be a QObject");

  if (root)
  {
    return qobject_cast_parent<U, Us...>(qobject_cast<U *>(root->parent()))
  }
  else
  {
    return nullptr;
  }
}

因此,模板参数从子级到父级排序,您也将被强制指定最里面的类型。所以我认为你的assert示例看起来像这样(再次,未经测试,让我知道它是如何工作的):

assert(qobject_cast_parent<CustomDialog,
                           QDockWidget,
                           QMainWindow,
                           CustomDockArea,
                           QDockWidget>(this));

编辑:为了完整起见,您还询问了一种获取参数包最后一个参数类型的方法。你可以使用这样的东西:

template <typename T, typename... Ts>
struct Last
{
  typedef typename Last<Ts...>::type type;
};

template <typename T>
struct Last<T>
{
  typedef T type;
};

int main()
{
  // For example, this gives std::string:
  Last<int, float, char, std::string>::type foo = "bar";
  return 0;
}