【问题标题】:Template deduction not working for function pointer reference模板推导不适用于函数指针引用
【发布时间】:2015-04-12 13:38:07
【问题描述】:

我正在使用 detours,我发现他们使用的演员表非常难看,所以我编写了几个模板函数来为我做演员表。

// 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++ 正确推导出函数指针?

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    &amp;DestroyIcon 是一个右值,所以* &amp;&amp; 参数将绑定到它,* &amp; 不会,这正是我们想要的。

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

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

    如果您不喜欢在函数名称前使用 &amp; 运算符,您还可以将采用 * &amp;&amp; 的重载更改为采用 &amp; - 对函数的左值引用。现在,该版本可以称为fnPtrToVoidPtr(DestroyIcon)&amp;&amp;(对函数的右值引用)也可以工作,因为对函数的右值引用也绑定到函数左值(所有指定函数的标识符表达式都是左值)。

    【讨论】:

    • 这几天我一直在尝试解决这个问题!如果必须强制类型,当然模板推导不起作用。这很有意义。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多