Pra*_*tic 6 c++ templates function-pointers sfinae c++11
在此示例中,函数被传递给隐式实例化的函数模板.
// Function that will be passed as argument
int foo() { return 0; }
// Function template to call passed function
template<typename F>
int call(F f) {
return f();
}
template<typename F, typename A>
int call(F f, A a) {
return f(a);
}
int a = call(foo);
Run Code Online (Sandbox Code Playgroud)
我们可以通过添加重载来破坏此代码foo().
int foo(int i) { return 0; }
Run Code Online (Sandbox Code Playgroud)
名称" foo"现在不明确,示例将不再编译.这可以通过显式提供函数指针类型信息来进行编译.
int (*func_takes_void)() = foo;
int a = call(func_takes_void);
int (*func_takes_int)(int) = foo;
int b = call(func_takes_int, 0);
Run Code Online (Sandbox Code Playgroud)
http://coliru.stacked-crooked.com/a/e08caf6a0ac1e6b9
是否可以推断出函数指针类型?如果是这样,为什么我的尝试下面不起作用,这是正确的方法?
如果这是不可能的,一个好的答案可以解释原因.
到目前为止尝试
foo()通过检查定义,人类可以看到两个调用中的目标,call<>()但编译器无法获得重载解析信息.尽管如此,信息仍然存在,只需将其拉入功能模板签名即可.这可能是表达SFINAE的可能.
在伪代码中我们想要这个:
template<IgnoreThis, typename ReturnType>
struct expr_check
{
typedef ReturnType type;
}
template<typename F>
expr_check<expression requiring F have correct signature, result_of<F>::type>::type
call(F f);
Run Code Online (Sandbox Code Playgroud)
这个想法是在真实的代码中解决的.
http://coliru.stacked-crooked.com/a/a3ce828d6cb16c2d
功能模板签名是:
template<typename F>
typename expr_check<sizeof(declval<F>()()), typename func_ptr_result<F>::type>::type
call(F f);
template<typename F, typename A>
typename expr_check<sizeof(declval<F>()(declval<A>())), typename func_ptr_result<F>::type>::type
call(F f, A a);
Run Code Online (Sandbox Code Playgroud)
我目前没有编译.从编译器输出中可以看出,在两次尝试实例化函数模板时,一个call<>()重载上存在替换失败,另一个只是给出了一个不透明的"无法推断模板参数".
(colirus编译为C++ 03,但C++ 11答案很好.)
我怀疑是在实例化时call<>(),foo()没有被调用,C++根本就没有提供foo()此上下文中的重载解析.可以证明一个foo()重载是正确的,C++并不强制要求重载解析.另一方面,重载决策不限于被调用的函数.适当类型的函数指针可以选择重载foo().
相关问题
有几个问题询问有关函数指针类型的重载.看起来这是无法做到的.通过表达SFINAE,我没有发现任何试图这样做的问题.
这似乎是最相关的问题.
奖金迂腐
"函数指针"是否在标题中使用了正确的短语?"功能参考"会更准确吗?
你可以得到的最接近的可能是:
struct sfoo
{
template<typename... args>
void operator() (args&&... a)
{
foo(std::forward<args>(a)...);
}
};
Run Code Online (Sandbox Code Playgroud)
并传递sfoo(或sfoo())而不是foo周围.
也就是说,创建一个函数对象类型,它封装了模板化中的整个重载集operator().
然后,不是对不存在的模板参数进行重载解析,而是通过相同的参数获得模板实例化,这是正常的.
正如之前提到的,SFINAE 不起作用,因为重载函数的名称在 C++ 中没有明确的类型,因此模板参数替换在这个阶段甚至不会发生。
但是,在您的示例中,问题可以说不是“foo”的重载太多,而是“call”的重载太少。只需提供类型名为 F 的模板和需要函数指针的模板即可。编译器现在将能够根据上下文执行正确的操作:
#include <iostream>
// Functions
int foo() { return 0; }
int foo(int) { return 1; }
// Function object
struct Foo
{
int operator()() const { return 2; }
int operator()(int) const { return 3; }
};
// Higher-order functions / templates
template<typename F>
int call(F f) {
return f();
}
int call(int (*f)()) {
return f();
}
template<typename F, typename A>
int call(F f, A a) {
return f(a);
}
template<typename A>
int call(int (*f)(A), A a) {
return f(a);
}
int main()
{
int a = call(foo)
, b = call(foo, 0)
, c = call(Foo())
, d = call(Foo(), 0);
std::cout << a << ',' << b << ',' << c << ',' << d << '\n'; // 0,1,2,3
}
Run Code Online (Sandbox Code Playgroud)
通过添加返回类型推导可以使调用重载更加通用。在 C++11 中,即使对于函数对象,也可以通过使用 decltype rsp 实现这一点。的结果。为了简洁起见,我将仅发布新的函数签名,因为在这种情况下不需要更改主体:
template<typename F>
auto call(F f) -> decltype(f());
template<typename R>
R call(R (*f)());
template<typename F, typename A>
auto call(F f, A a) -> decltype(f(a));
template<typename R, typename A>
R call(R (*f)(A), A a);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
456 次 |
| 最近记录: |