有没有一种方法可以在传递左值时指定不同的函数,而在不传递左值时指定另一个函数?
Is there a way to specify a different function when passed a lvalue and another when not?
如果我使用的术语不正确,请提前致歉。
我想要 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很丑,所以我写了几个模板函数帮我做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 *&`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);
编辑: 此处不需要 std::forward
,但它只是为了展示更多 "transparent" 值传递函数的视图。
需要时使用 & 和 &&,其中 & = 左值,&& = 右值。
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;
}
这里唯一的问题是您在使用 typedef 时搞乱了间接寻址的数量。我已经在 this associated sample.
中解决了这个问题
总之,之所以不行,是因为你拿了一个fn_t*&
。值 pFn
只是一个 fn_t
,而不是 fn_t*
。这就是引用不能引用它的原因。由于 fn_t
已经包含指针部分,因此您不需要添加额外的指针。
// 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;
}
是我想你想要的。右值得到 &&
左值 &
并且没有检测到歧义。
现在上面的缺点是 &&
案例无法正确推断签名。我可以想出办法,或者我可以使用蛮力来避免这个问题。我们将两者移动到一个 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...)
的适配器,或者以其他方式解决该问题。
如果我使用的术语不正确,请提前致歉。
我想要 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很丑,所以我写了几个模板函数帮我做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 *&`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);
编辑: 此处不需要 std::forward
,但它只是为了展示更多 "transparent" 值传递函数的视图。
需要时使用 & 和 &&,其中 & = 左值,&& = 右值。
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;
}
这里唯一的问题是您在使用 typedef 时搞乱了间接寻址的数量。我已经在 this associated sample.
中解决了这个问题总之,之所以不行,是因为你拿了一个fn_t*&
。值 pFn
只是一个 fn_t
,而不是 fn_t*
。这就是引用不能引用它的原因。由于 fn_t
已经包含指针部分,因此您不需要添加额外的指针。
// 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;
}
是我想你想要的。右值得到 &&
左值 &
并且没有检测到歧义。
现在上面的缺点是 &&
案例无法正确推断签名。我可以想出办法,或者我可以使用蛮力来避免这个问题。我们将两者移动到一个 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...)
的适配器,或者以其他方式解决该问题。