有没有办法在传递一个左值时指定一个不同的函数而另一个没有?

时间:2015-04-12 12:15:42

标签: c++ c++11 sfinae

如果我没有使用正确的术语,请提前道歉。

我希望有2个具有相同名称​​的函数,一个在值为左值时调用,另一个在不是左值时调用。例如:

typedef void (*fn_t)();
void fn() { }

fn_t pFn = fn;

fn_t* distinguish(fn_t pFn)
{
  return pFn;
}

// NOTE: Will not work
fn_t*& distinguish(fn_t& pFn)
{
  return pFn;
}

fn_t*& distinguish1(fn_t& pFn)
{
  return pFn;
}

int main()
{
  // call the one that takes a parameter from which I cannot get the address of the function pointer
  distinguish(fn);

  fn_t pFn = fn;
// call the one that takes a parameter from which I can get the address of the function pointer
  distinguish(pFn);
  distinguish1(pFn); // this will work
  distinguish1(fn); // this will not work
  return 0;
}

这可能吗?也许我可以使用SFINAE?

我正在尝试做什么:

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

// 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 *&`anonymous-namespace'::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);

3 个答案:

答案 0 :(得分:2)

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

我认为你想要什么。 rvalue获得&&左值&并且未检测到歧义。

现在上面的缺点是&&案例不会正确推断签名。我可以弄清楚如何,或者我可以使用蛮力来避免这个问题。我们将两者移动到details命名空间,我们添加标签类型以在两者之间进行分派,我们删除&&,然后我们添加一个调度函数,如下所示:

namespace details {
  template <typename RET_TYPE, typename...ARGs>
  void* fnPtrToVoidPtr(std::true_type, 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(std::false_type, RET_TYPE(WINAPI*& pOriginalFunction)(ARGs...)) {
    return (void*&)pOriginalFunction;
  }

}
template<class T>
auto fnPtrToVoidPtr(T&& t)
-> decltype(
  details::fnPtrToVoidPtr(
    std::is_rvalue_reference<T&&>{}, std::forward<T>(t)
  )
) {
  return
  details::fnPtrToVoidPtr(
    std::is_rvalue_reference<T&&>{}, std::forward<T>(t)
  );
}
是的,多么糟糕。

要使上述工作正常,如果&foo是函数名称,则需要传入foo。如果仅传入foo,则无法编译。如果需要,您可以编写一个将R(&)(Args...)映射到R(*)(Args...)的适配器,或以其他方式修复该怪癖。

live example

答案 1 :(得分:1)

这里唯一的问题是你弄乱了你的typedef使用的间接数量。我已在this associated sample中解决了该问题。

简而言之,它之所以不起作用,是因为你选择了fn_t*&。值pFn仅为fn_t,而非fn_t*。这就是参考不能引用它的原因。由于fn_t已包含指针部分,因此您无需向其添加其他指针。

答案 2 :(得分:0)

修改:您在这里不需要std::forward,但它只是为了展示更多&#34;透明&#34;通过函数的值的视图。

使用&amp;和&amp;&amp;在需要的时候,&amp; =左值和&amp;&amp; = rvalue。

http://coliru.stacked-crooked.com/a/81539ac10b8be5a1

#include <iostream>
#include <utility>

int& forward(int& i)
{
    std::cout << "lvalue: ";
    return std::forward<int&>(i);   
}

int&& forward(int&& i)
{
    std::cout << "rvalue: ";
    return std::forward<int&&>(i);    
}

int*&& forward(int*&& ptr_i)
{
    std::cout << "rvalue ptr: ";
    return std::forward<int*&&>(ptr_i);   
}

int*& forward(int*& ptr_i)
{
    std::cout << "lvalue ptr: ";
    return std::forward<int*&>(ptr_i);   
}

const int* forward(const int* const  ptr_to_const)
{
    std::cout << "const ptr to lvalue: ";
    return std::forward<const int* const>(ptr_to_const);
}

int main()
{
    int i = 1;

    const int j = 13;

    int* ptr_i = &i;

    const int * const  const_ptr_const_i = &j;

    int * const const_ptr_i = &i;


    std::cout << forward(0) << std::endl;
    std::cout << forward(i) << std::endl;
    std::cout << forward(ptr_i) << std::endl;
    std::cout << forward(&i) << std::endl;

    //These two are almost the same by nature; note the variables they point to are different.
    std::cout << forward(const_ptr_const_i) << std::endl;
    std::cout << forward(const_ptr_i) << std::endl;

    std::cout << "Cannot have a const pointer to a const rvalue because you can't really have a const pointer to a temporary/disappearing/ephermal rvalue." << std::endl;


    return 0;
}