一个自定义的MFC窗口/对话框可以是一个类模板实例化吗?

3

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那样。
我假设有人过去肯定已经做过这件事,可能创建了自定义版本的宏,但我无法通过搜索找到它,因为“template”还有其他含义。

你遇到了哪些错误? - rkellerm
4个回答

3
你可能还有其他问题,但其中一个问题是你使用了 super。这是Java中的语法,而不是C ++。你需要使用 CDialog 代替 super
在研究 IMPLEMENT_DYNAMIC 后发现它与模板不兼容,它没有在函数定义之前使用 template <class T> 语法。你需要定义模板的派生类特化,然后在它们上使用该宏。所以你可以这样做:
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关键字)。 - Mr. Boy
如果你想这样做,那没问题,但是如果你在编译时出现错误,我建议你先尝试不使用它,以确保它没有影响。但是我的答案的第二部分肯定也会给你带来问题。 - bshields
嗯,专业化有点违背模板的初衷。我可以看一下宏,但问题是有相当多的宏互相调用... 真恶心。 - Mr. Boy
你必须在某个时候进行专业化。请注意,这不一定发生在定义CMyDlg模板类的同一个头文件中。唯一比正常情况更麻烦的是,你需要创建一个空的派生类,而不仅仅是一个typedef,因为你需要能够使用IMPLEMENT_DYNAMIC宏来定义该类的方法。你知道你可以制作自己的宏,定义空的派生类并调用IMPLEMENT_DYNAMIC宏,然后你可以在任何你通常会放置模板实例化的typedef的地方使用它。 - bshields
有趣的想法,我会记在心里,不过如果可能的话,我更喜欢得到一个适用于模板的宏集合。 - Mr. Boy

2

2
这里有一个可行的解决方案,虽然不太美观...我还没有将其重写为宏,在扩展现有解决方案并修复模板后就束手无策了。
//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); }

0

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 的问题,但它们应该是类似的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接