【问题标题】:Can a custom MFC window/dialog be a class template instantiation?自定义 MFC 窗口/对话框可以是类模板实例化吗?
【发布时间】:2010-06-09 10:16:16
【问题描述】:

MFC 在创建对话框时使用了一堆特殊的宏,在我的快速测试中,我在尝试编译模板对话框类时遇到了奇怪的错误。这可能是一个很大的痛苦实现吗?

这是我尝试过的:

MyDlg.h

template <class W>
class CMyDlg : public CDialog
{
    typedef CDialog super;
    DECLARE_DYNAMIC(CMyDlg <W>)

public:
    CMyDlg (CWnd* pParent);   // standard constructor
    virtual ~CMyDlg ();

// Dialog Data
    enum { IDD = IDD_MYDLG };

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

    DECLARE_MESSAGE_MAP()

private:
    W *m_pWidget; //W will always be a CDialog
};



IMPLEMENT_DYNAMIC(CMyDlg<W>, super) <-------------------

template <class W>
CMyDlg<W>::CMyDlg(CWnd* pParent)
    : super(CMyDlg::IDD, pParent)
{
  m_pWidget = new W(this);
}

我收到一大堆错误,但主要错误似乎是:

错误 C2955: 'CMyDlg' : 使用类 模板需要模板参数 列表

我尝试使用一些专门的宏模板版本,但没有多大帮助,其他错误发生了变化,但这个错误仍然存​​在。请注意,我的代码都在一个文件中,因为 C++ 模板不像普通的那样喜欢 .h/.cpp。

我假设过去一定有人这样做过,可能创建了自定义版本的宏,但我无法通过搜索找到它,因为“模板”还有其他含义。

【问题讨论】:

  • 您遇到了哪些错误?

标签: c++ templates mfc


【解决方案1】:

您可能还有其他问题,但有一件事必须是您对super 的使用。那是 java 的东西,而不是 C++ 的东西。你需要使用CDialog,而不是super

在查看IMPLEMENT_DYNAMIC 后,宏定义与模板不兼容,它没有在函数定义之前使用template &lt;class T&gt; 语法。您需要做的是定义模板的派生类特化,然后在它们上使用宏。所以你可以这样做:

class MyDlgA : public CMyDlg<A>
{
};

IMPLEMENT_DYNAMIC(MyDlgA, CDialog);

然后针对您想要的所有专业进行此操作。如果这不可行,请查看宏并制作您自己的模板化版本。

编辑: 跟进我的评论,您可以制作这样的宏:

#define INSTANTIATE_DLG_TEMPLATE(T)  \
class MyDlg##T : public CMyDlg<T>    \
{                                    \
};                                   \
                                     \
IMPLEMENT_DYNAMIC(MyDlg##T, CDialog);

然后只要在通常使用 typedef 在头文件中定义模板特化的任何地方使用它。

【讨论】:

  • 为方便起见,我自己定义了这个。效果很好(顺便说一下,MSVC 有一个 __super 关键字)
  • 好的,如果您想这样做,如果您遇到编译错误以确保它没有发挥作用,我仍然会在没有它的情况下尝试它。但我回答的第二部分肯定也会给你带来问题。
  • 嗯,专业化会取消模板的全部意义。我可以查看宏,但问题是有很多都互相调用...呸。
  • 嗯,你必须在某个时候专攻。请注意,这不必出现在定义 CMyDlg 模板类的同一个标头中。这里唯一比正常情况更麻烦的是您需要创建一个空的派生类而不仅仅是一个 typedef,因为您需要能够使用 IMPLEMENT_DYNAMIC 宏为该类定义方法。您知道您可以创建自己的宏来定义空派生类并调用 IMPLEMENT_DYNAMIC 宏,并且您可以在通常将 typedef 用于模板实例化的任何地方使用它。
  • 有趣的想法,我会记住的,尽管我更喜欢尽可能获得一组模板友好的宏。
【解决方案2】:

我没有为对话框执行此操作,仅针对某些自定义控件,但我看不出它为什么不起作用。
我知道至少有一个用于定义消息映射的模板版本,BEGIN_TEMPLATE_MESSAGE_MAP。 查看http://msdn.microsoft.com/en-us/library/aa991537(VS.80).aspx

【讨论】:

    【解决方案3】:

    这是一个可行的解决方案,虽然丑陋...在扩展现有宏并修复模板后,我没有考虑重写为宏:

    //Template-enabled expansion of IMPLEMENT_DYNAMIC(CMyDlg,super)
    template <class W> CRuntimeClass* PASCAL CMyDlg<W>::_GetBaseClass(){ return RUNTIME_CLASS(super); }
    template <class W> AFX_COMDAT const CRuntimeClass CMyDlg<W>::CMyDlg= {
            "CMyDlg", sizeof(CMyDlg<W>), 0xFFFF, NULL,&CMyDlg<W>::_GetBaseClass, NULL, NULL };
    template <class W> CRuntimeClass* PASCAL CMyDlg<W>::GetThisClass()  { return _RUNTIME_CLASS(CMyDlg); }
    template <class W> CRuntimeClass* CMyDlg<W>::GetRuntimeClass() const { return _RUNTIME_CLASS(CMyDlg); }
    

    【讨论】:

      【解决方案4】:

      由于 DECLARE_DYNAMIC 宏,会出现许多问题。如果你跟踪宏,你会发现必须定义一个成员变量和三个函数。

      template<typename T>
      class CTemplateDialogDlg : public CDialogEx
      {
          // Construction
      public:
          // standard constructor
          CTemplateDialogDlg( CWnd* pParent = nullptr )
              : CDialogEx( IDD_TEMPLATEDIALOGAPP_DIALOG, pParent )
          {}
      
      // Dialog Data
      #ifdef AFX_DESIGN_TIME
          enum
          {
              IDD = IDD_TEMPLATEDIALOGAPP_DIALOG
          };
      #endif
      public:
          T m_tMemberValue;
      
      protected:
          // DDX/DDV support
          virtual void DoDataExchange( CDataExchange* pDX )
          {
              CDialogEx::DoDataExchange( pDX );
          }
      
      protected:
          // Member & Functions from IMPLEMENT_DYNAMIC( CUnitTypeCurvePointDlg, CDialogEx )
      
          static const CRuntimeClass classCTemplateDialogDlg;
          static CRuntimeClass* PASCAL _GetBaseClass()
          {
              return RUNTIME_CLASS( CDialogEx );
          }
          static CRuntimeClass* PASCAL GetThisClass()
          {
              return (CRuntimeClass*)&classCTemplateDialogDlg;
          }
          virtual CRuntimeClass* GetRuntimeClass() const
          {
              return (CRuntimeClass*)&classCTemplateDialogDlg;
          }
      };
      

      然后必须创建成员变量(仅一次)

      typedef CTemplateDialogDlg<int> CTemplateDialogIntDlg;
      const CRuntimeClass CTemplateDialogIntDlg::classCTemplateDialogDlg;
      

      然后就可以使用模板对话框了

      CTemplateDialogIntDlg Dlg;
      

      但是,由于这会绕过 MFC 宏,因此您将负责正确定义成员变量和函数。

      我还没有解决 DECLARE_MESSAGE_MAP 问题,但它们会很相似。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多