模板推导不适用于函数指针引用

时间:2015-04-12 13:38:08

标签: c++ templates c++11 template-deduction

我正在使用弯路而且我发现他们使用的演员非常难看,所以我已经写了几个模板函数来为我做演员。

// Cast a function pointer to a void *
template <typename RET_TYPE, typename...ARGs>
void* fnPtrToVoidPtr(RET_TYPE(WINAPI * pOriginalFunction)(ARGs...))
{
    return (void*)pOriginalFunction;
}

// Cast a function pointer that is referencable to a void *&
template <typename RET_TYPE, typename...ARGs>
void*& fnPtrRefToVoidPtrRef(RET_TYPE(WINAPI*& pOriginalFunction)(ARGs...))
{
    return (void*&)pOriginalFunction;
}

这允许我进行以下调用:

BOOL (WINAPI *pDestroyIcon)(HICON) = DestroyIcon;
DetourAttach(&fnPtrRefToVoidPtrRef(pDestroyIcon), fnPtrToVoidPtr(DestroyIcon));

但是,我想知道是否可以将两个函数名称fnPtrRefToVoidPtrReffnPtrToVoidPtr合并为一个名称。

执行以下操作并不起作用,因为它无法推断出模板参数:

// Cast a function pointer to a void *
template <typename RET_TYPE, typename...ARGs>
void* fnPtrToVoidPtr(RET_TYPE(WINAPI * & pOriginalFunction)(ARGs...))
{
    return (void*)pOriginalFunction;
}

// Cast a function pointer that is referencable to a void *&
template <typename RET_TYPE, typename...ARGs>
void*& fnPtrToVoidPtr(RET_TYPE(WINAPI * && pOriginalFunction)(ARGs...))
{
    return (void*&)pOriginalFunction;
}

BOOL (WINAPI *pDestroyIcon)(HICON) = DestroyIcon;
void* p1 = fnPtrToVoidPtr(DestroyIcon);
void** p2 = &fnPtrToVoidPtr(pDestroyIcon);

导致以下错误:

// error C2784: 'void *&fnPtrToVoidPtr(RET_TYPE (__stdcall *&&)(ARGs...))' : could not deduce template argument for 'overloaded function type' from 'overloaded function type'

使用我原来的功能,这很好用:

    BOOL (WINAPI *pDestroyIcon)(HICON) = DestroyIcon;
    void* p1 = fnPtrToVoidPtr(DestroyIcon);
    void** p2 = &fnPtrRefToVoidPtrRef(pDestroyIcon);

但是,如果我将fnPtrRefToVoidPtrRef更改为:

// Cast a function pointer that is referencable to a void *&
template <typename RET_TYPE, typename...ARGs>
void*& fnPtrRefToVoidPtrRef(RET_TYPE(WINAPI*&& pOriginalFunction)(ARGs...))
{
    return (void*&)pOriginalFunction;
}

我收到以下错误:

error C2664: 'void *&fnPtrRefToVoidPtrRef<BOOL,HICON>(RET_TYPE (__stdcall *&&)(HICON))' : cannot convert argument 1 from 'BOOL (__stdcall *)(HICON)' to 'BOOL (__stdcall *&&)(HICON)'

这似乎是为什么它不能进行模板推导,它不能识别它是相同的(或可兑换的?)类型。有没有办法让C ++正确推导出函数指针?

1 个答案:

答案 0 :(得分:2)

您的代码存在两个问题。让我们按顺序修复它们。

首先,必须切换两个fnPtrToVoidPtr重载的主体和返回类型。你想要的是通过void*强制转换将函数指针类型的左值转换为(void*&)类型的左值,所以这个强制转换应该放在函数体中,取* & - 这个是将绑定到可修改左值的参数类型,另一个* &&将绑定到右值。相反,(void*)演员必须使用* &&进入函数。显然,返回类型需要相应更改。

现在,演绎失败的原因是:在原始版本中进行fnPtrToVoidPtr(DestroyIcon)之类的调用时,您依赖于函数到指针的转换。如果参数(目标)是引用,则不会发生此转换。

所以,在你的第二个版本中,两个重载都接受引用,参数是对指针的引用,但参数是一个函数标识符;前者的模板参数不能从后者推导出来,因此演绎失败。对此问题最简单的解决方法是显式提供调用的函数指针,如下所示:fnPtrToVoidPtr(&DestroyIcon)

&DestroyIcon是一个右值,因此* &&参数会绑定到它,* &将不会,正是我们想要的。

通过这两个修复,代码的可编译版本变为:

#include "windows.h"

// Cast a function pointer to a void *
template <typename RET_TYPE, typename...ARGs>
void* fnPtrToVoidPtr(RET_TYPE(WINAPI * && pOriginalFunction)(ARGs...))
{
   return (void*)pOriginalFunction;
}

// Cast a function pointer that is referencable to a void *&
template <typename RET_TYPE, typename...ARGs>
void*& fnPtrToVoidPtr(RET_TYPE(WINAPI * & pOriginalFunction)(ARGs...))
{
   return (void*&)pOriginalFunction;
}

BOOL(WINAPI *pDestroyIcon)(HICON) = DestroyIcon;

int main()
{
   void* p1 = fnPtrToVoidPtr(&DestroyIcon);
   void** p2 = &fnPtrToVoidPtr(pDestroyIcon);
}

如果您不希望在函数名称前面使用&运算符,您还可以更改过载,* &&采用& - 左值参考一个功能。现在,该版本可以称为fnPtrToVoidPtr(DestroyIcon)&&(对函数的rvalue引用)也可以工作,因为对函数的rvalue引用也绑定到函数左值(所有标识符表达式指定函数都是左值)。