模板推导不适用于函数指针引用
Template deduction not working for function pointer reference
我走弯路,发现他们用的cast很丑,所以我写了几个模板函数帮我做cast。
// 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));
但是,我想知道是否可以将两个函数名称 fnPtrRefToVoidPtrRef
和 fnPtrToVoidPtr
合并为一个名称。
执行以下操作无效,因为它无法推断模板参数:
// 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++ 正确推导函数指针?
您的代码有两个问题。让我们按顺序修复它们。
首先,必须切换两个 fnPtrToVoidPtr
重载的主体和 return 类型。您想要的是通过 (void*&)
强制转换将函数指针类型的左值转换为 void*
类型的左值,因此此强制转换应该进入采用 * &
的函数主体 - 这个是将绑定到可修改左值的参数类型,另一个 * &&
将绑定到右值。相反,(void*)
转换必须进入采用 * &&
的函数。显然,return 类型需要相应地更改。
现在,推导失败的原因是:在您的原始版本中进行类似 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)
。 &&
(对函数的右值引用)也可以工作,因为对函数的右值引用也绑定到函数左值(所有指定函数的标识符表达式都是左值)。
我走弯路,发现他们用的cast很丑,所以我写了几个模板函数帮我做cast。
// 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));
但是,我想知道是否可以将两个函数名称 fnPtrRefToVoidPtrRef
和 fnPtrToVoidPtr
合并为一个名称。
执行以下操作无效,因为它无法推断模板参数:
// 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++ 正确推导函数指针?
您的代码有两个问题。让我们按顺序修复它们。
首先,必须切换两个 fnPtrToVoidPtr
重载的主体和 return 类型。您想要的是通过 (void*&)
强制转换将函数指针类型的左值转换为 void*
类型的左值,因此此强制转换应该进入采用 * &
的函数主体 - 这个是将绑定到可修改左值的参数类型,另一个 * &&
将绑定到右值。相反,(void*)
转换必须进入采用 * &&
的函数。显然,return 类型需要相应地更改。
现在,推导失败的原因是:在您的原始版本中进行类似 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)
。 &&
(对函数的右值引用)也可以工作,因为对函数的右值引用也绑定到函数左值(所有指定函数的标识符表达式都是左值)。