模板推导不能用于函数指针引用 [英] Template deduction not working for function pointer reference

查看:199
本文介绍了模板推导不能用于函数指针引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用detours,我发现他们使用非常丑陋的演员,所以我写了几个模板功能来为我铸造。

  //将函数指针转换为void * 
模板< typename RET_TYPE,typename ... ARGs>
void * fnPtrToVoidPtr(RET_TYPE(WINAPI * pOriginalFunction)(ARGs ...))
{
return(void *)pOriginalFunction;
}

//投入一个可以引用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 <$ c $ p <$> fnPtrToVoidPtr 合并为一个名称。



't推导模板参数:

  //将函数指针转换为void * 
template< typename RET_TYPE ,typename ... ARGs>
void * fnPtrToVoidPtr(RET_TYPE(WINAPI *& pOriginalFunction)(ARGs ...))
{
return(void *)pOriginalFunction;
}

//投入一个可以引用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);

导致以下错误:

  //错误C2784:'void *& fnPtrToVoidPtr(RET_TYPE(__stdcall *&&)(ARGs ...))':无法推导'overloaded function type'的模板参数'重载函数类型'

使用我的原始函数,这很好:

  BOOL(WINAPI * pDestroyIcon)(HICON)= DestroyIcon; 
void * p1 = fnPtrToVoidPtr(DestroyIcon);
void ** p2 =& fnPtrRefToVoidPtrRef(pDestroyIcon);但是,如果我将 fnPtrRefToVoidPtrRef 更改为此



  //投一个可以引用void *的函数指针。 
template< typename RET_TYPE,typename ... ARGs>
void *& fnPtrRefToVoidPtrRef(RET_TYPE(WINAPI *& pOriginalFunction)(ARGs ...))
{
return(void *&)pOriginalFunction;
}

我收到以下错误:

 错误C2664:'void *& fnPtrRefToVoidPtrRef< BOOL,HICON>(RET_TYPE(__stdcall *&&)(HICON))':无法将参数1从' BOOL(__stdcall *)(HICON)'至'BOOL(__stdcall *&&)(HICON)'

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

解决方案

你的代码有两个问题。让我们按顺序修复它们。



首先,两个 fnPtrToVoidPtr 重载的主体和返回类型必须切换。你想要的是通过(void *&) void * c> cast,所以这个转换应该在函数体中带一个 *& - 这是将绑定到可修改的左值的参数类型,另一个, *&&& 将绑定到右值。相反,(void *) cast必须进入函数取一个 *&& 。显然,返回类型需要相应地改变。



现在,扣除失败的原因:当调用 fnPtrToVoidPtr(DestroyIcon) 在你的原始版本,你依赖于函数到指针的转换。如果参数(目标)是参考,则不会发生此转换。



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



& DestroyIcon 是一个右值,因此 *&&& 参数会绑定到它



有了这两个修复,一个可编译的版本您的代码变为:

  #includewindows.h

//将函数指针a void *
template< typename RET_TYPE,typename ... ARGs>
void * fnPtrToVoidPtr(RET_TYPE(WINAPI *& pOriginalFunction)(ARGs ...))
{
return(void *)pOriginalFunction;
}

//投入一个可以引用void *的函数指针。
模板< 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)。 A &&& (对函数的右值引用)也将工作,因为对函数的右值引用也绑定到函数l值(所有标识符表达式指定函数都是左值)。


I'm using detours and I find that the cast that they use very ugly, so I've written a couple of template functions to do the casting for me.

// 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;
}

This allows me to do the following call:

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

However, I was wondering if I could consolidate the two function names fnPtrRefToVoidPtrRef and fnPtrToVoidPtr into one name.

Doing the following doesn't work as it can't deduce the template arguments:

// 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);

causing the following error:

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

Using my original functions, this works fine:

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

However, if I change fnPtrRefToVoidPtrRef to this:

// 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;
}

I get the following error:

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

Which seems to be why it can't do template deduction, it doesn't recognize it to be the same (or convertible?) type. Is there a way to get C++ to properly deduce the function pointer?

解决方案

There are two problems with your code. Let's fix them in order.

First, the bodies and return types of the two fnPtrToVoidPtr overloads have to be switched. What you want is to convert lvalues of function pointer type to lvalues of type void*, through the (void*&) cast, so this cast should go in the body of the function taking a * & - this is the parameter type that will bind to modifiable lvalues, the other one, * && will bind to rvalues. Conversely, the (void*) cast has to go into the function taking a * &&. Obviously, the return types need to be changed accordingly.

Now, the reason for the deduction failure: when making a call like fnPtrToVoidPtr(DestroyIcon) in your original version, you're relying on the function-to-pointer conversion. This conversion doesn't happen if the parameter (destination) is a reference.

So, in your second version, where both overloads take references, the parameters are references to pointers, but the argument is a function identifier; template arguments for the former cannot be deduced from the latter, so deduction fails. The simplest fix for this one is to explicitly provide a function pointer for the call, like this: fnPtrToVoidPtr(&DestroyIcon).

&DestroyIcon is an rvalue, so the * && parameter will bind to it, * & won't, exactly what we want.

With these two fixes, a compilable version of your code becomes:

#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);
}

If you don't like having to use the & operator in front of function names, you can also change the overload taking a * && to take a & - an lvalue reference to a function. Now, that version can be called as fnPtrToVoidPtr(DestroyIcon). A && (rvalue reference to function) will work too, since rvalue references to functions bind to function lvalues as well (all identifier expressions designating functions are lvalues).

这篇关于模板推导不能用于函数指针引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆