您可以在 C++11 中实现这一点:
#include <type_traits>
#include <iostream>
template <typename Param, typename Arg>
Param take_address_if_necessary_impl (Arg&& arg, std::true_type, std::false_type)
{
return arg;
}
template <typename Param, typename Arg>
Param take_address_if_necessary_impl (Arg&& arg, std::false_type, std::true_type)
{
return &arg;
}
template <typename Param, typename Arg>
Param take_address_if_necessary (Arg&& arg)
{
return take_address_if_necessary_impl <Param> (
arg,
typename std::is_convertible <Arg, Param>::type {},
typename std::is_convertible <typename std::add_pointer <Arg>::type, Param>::type {}
);
}
template <typename Ret, typename... Params, typename... Args>
Ret call_special (Ret (*f) (Params...), Args&&... args)
{
return f (take_address_if_necessary <Params, Args> (args)...);
}
template <typename... Params, typename... Args>
void call_special (void (*f) (Params...), Args&&... args)
{
f (take_address_if_necessary <Params> (args)...);
}
void function (int* i, char* c)
{
std::cout << *i << ' ' << *c << std::endl;
}
int main ()
{
int i = 42;
char c = '%';
call_special (function, 1, 'f');
call_special (function, &i, '?');
call_special (function, &i, &c);
}
上述程序产生
1 f
42 ?
42 %
如你所愿。
这里有一些注意事项:首先,如果您尝试使用重载函数,这将失败,因为 C++ 无法将重载函数推断为函数指针:
void bar (int);
void bar (float);
call_special (bar, 3.0f); // Compiler error
您也许可以使用显式模板参数来解决此问题:
call_special <float> (bar, 3.0f); // Works
或者当然是显式输入函数:
call_special ((void (*) (float))bar, 3.0f);
其次,为简单起见,call_special 及其助手在值类上玩得又快又松。可能需要做更多工作才能稳健地处理右值、常量值等。
第三,既可以作为值也可以作为指针传递的参数将不起作用。事实上,一般来说,转换可能会让人头疼:
void quux (long* i)
{
if (i)
std::cout << *i << std::endl;
else
std::cout << "(null)" << std::endl;
}
call_special (quux, NULL); // What does this print?
你最好使用一个函数来明确地获取地址:
template <typename T>
T* foo (T&& t)
{
return &t;
}
function (foo (3), foo ('7'));
请注意,无论您使用哪种方法,您都将处理临时对象,它们以分号结尾。因此,如果您使用的库存储了对您给它的参数的引用,那么您别无选择,只能显式地为参数提供存储空间。