c++ - 有没有更好的方法来选择正确的方法重载?

标签 c++ visual-studio-2008 templates mfc winapi

这真的是获取实例函数正确地址的唯一方法吗:

typedef CBitmap * (CDC::* SelectObjectBitmap)(CBitmap*);
SelectObjectBitmap pmf = (SelectObjectBitmap)&CDC::SelectObject;

首先,必须创建一个 typedef,然后必须使用它来强制编译器在获取其地址时选择正确的重载方法?

有没有更自然、更自包含的语法,例如:

SelecdtObjectBitmap pmf = &CDC::SelectObject(CBitmap*);

我使用 ScopeGuard经常在我的代码中。一个明显的用途是确保首先将任何临时 CDC 对象选入给定的 DC,然后在范围退出时将其删除,即使在异常情况下也能使我的代码无泄漏——并同时清理编写的代码(愚蠢的多退出路径并尝试/catch 等尝试处理从给定 CDC 中删除任何选定的对象)。

因此,我目前被迫做的事情的更完整示例如下所示:

// get our client rect
CRect rcClient;
GetClientRect(rcClient);

// get the real DC we're drawing on
PAINTSTRUCT ps;
CDC * pDrawContext = BeginPaint(&ps);

// create a drawing buffer
CBitmap canvas;
canvas.CreateCompatibleBitmap(pDrawContext, rcClient.Width(), rcClient.Height());

CDC memdc;
memdc.CreateCompatibleDC(pDrawContext);

//*** HERE'S THE LINE THAT REALLY USES THE TYPEDEF WHICH i WISH TO ELIMINATE ***//
ScopeGuard guard_canvas = MakeObjGuard(memdc, (SelectObjectBitmap)&CDC::SelectObject, memdc.SelectObject(&canvas));

// copy the image to screen
pDrawContext->BitBlt(rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height(), &memdc, rcClient.left, rcClient.top, SRCCOPY);

// display updated
EndPaint(&ps);

我总是觉得我很愚蠢,我需要对每个我想获取地址的重载函数进行类型定义。

那么...还有更好的方法吗?!

编辑:根据人们提供的答案,我相信我有一个解决我的潜在需求的方法:即为 MakeGuard 提供更自然的语法,为我推导出正确的 SelectObject 覆盖:

template <class GDIObj>
ObjScopeGuardImpl1<CDC, GDIObj*(CDC::*)(GDIObj*), GDIObj*> MakeSelectObjectGuard(CDC & dc, GDIObj * pGDIObj)
{
    return ObjScopeGuardImpl1<CDC, GDIObj*(CDC::*)(GDIObj*), GDIObj*>::MakeObjGuard(dc, (GDIObj*(CDC::*)(GDIObj*))&CDC::SelectObject, dc.SelectObject(pGDIObj));
}

这使我的上述代码更改为:

ScopeGuard guard_canvas = MakeSelectObjectGuard(memdc, &canvas);

///////////////////////////////////////////////////////

对于那些可能会在这里寻找同一事物的非 MFC 版本的人:

//////////////////////////////////////////////////////////////////////////
//
// AutoSelectGDIObject
//  selects a given HGDIOBJ into a given HDC,
//  and automatically reverses the operation at scope exit
//
// AKA:
//  "Tired of tripping over the same stupid code year after year"
//
// Example 1:
//  CFont f;
//  f.CreateIndirect(&lf);
//  AutoSelectGDIObject select_font(*pDC, f);
//
// Example 2:
//  HFONT hf = ::CreateFontIndirect(&lf);
//  AutoSelectGDIObject select_font(hdc, hf);
//
// NOTE:
//  Do NOT use this with an HREGION.  Those don't need to be swapped with what's in the DC.
//////////////////////////////////////////////////////////////////////////

class AutoSelectGDIObject
{
public:
    AutoSelectGDIObject(HDC hdc, HGDIOBJ gdiobj) 
        : m_hdc(hdc)
        , m_gdiobj(gdiobj)
        , m_oldobj(::SelectObject(m_hdc, gdiobj))
    {
        ASSERT(m_oldobj != m_gdiobj);
    }

    ~AutoSelectGDIObject()
    {
        VERIFY(m_gdiobj == ::SelectObject(m_hdc, m_oldobj));
    }

private:
    const HDC       m_hdc;
    const HGDIOBJ   m_gdiobj;
    const HGDIOBJ   m_oldobj;
};

///////////////////////////////////////////////////////

感谢所有回复和评论的人! :D

最佳答案

你问的与之前的问题类似,我在那里给出的答案也与这里相关。

来自第 13.4/1 节(“重载函数的地址”,[over.over]):

A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. The function selected is the one whose type matches the target type required in the context. The target can be

  • an object or reference being initialized (8.5, 8.5.3),
  • the left side of an assignment (5.17),
  • a parameter of a function (5.2.2),
  • a parameter of a user-defined operator (13.5),
  • the return value of a function, operator function, or conversion (6.6.3), or
  • an explicit type conversion (5.2.3, 5.2.9, 5.4).

The overload function name can be preceded by the & operator. An overloaded function name shall not be used without arguments in contexts other than those listed. [Note: any redundant set of parentheses surrounding the overloaded function name is ignored (5.1). ]

在你的例子中,上面列表中的目标是第三个,你的MakeObjGuard的参数。功能。但是,我怀疑这是一个函数模板,模板的类型参数之一是函数指针的类型。编译器有一个 Catch-22。它无法在不知道选择了哪个重载的情况下推断出模板参数类型,也无法在不知道参数类型的情况下自动选择您所指的哪个重载。

因此,你需要帮助它。您可以像现在一样对方法指针进行类型转换,也可以在调用函数时显式指定模板参数类型:MakeObjGuard<SelectObjectBitmap>(...) .无论哪种方式,您都需要知道类型。您不需要为函数类型指定一个 typedef 名称,但它确实有助于提高可读性。

关于c++ - 有没有更好的方法来选择正确的方法重载?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2065228/

相关文章:

c++ - 可以使用自动占位符来推断非类型模板参数中的函数结果吗?

c++ - 如何专门化字符串数组的模板?

visual-studio - 有什么方法可以从命令行构建 Visual Studio "project only"?

c - 在 C 程序的 MS Access 查询中使用变量

visual-studio - Visual Studio 自定义工具列表

python - 在 Python 中获取网站描述的最佳方法是什么?

c++ - 继承返回 *this 的成员函数

c++ - 如何从 vector 类派生

c++ - 对话过程未收到 WM_COMMAND

c++ - 为什么会有定义_tmain 的宏?