表达式SFINAE重载类型的传递函数指针 [英] Expression SFINAE to overload on type of passed function pointer

查看:250
本文介绍了表达式SFINAE重载类型的传递函数指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这个例子中,函数被传递给一个隐式实例化的函数模板。

  //将作为参数传递的函数
int foo(){return 0; }

//函数模板调用传递函数
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);

我们可以通过添加 foo code>。

  int foo(int i){return 0; } 

现在名为 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);

http://coliru.stacked-crooked.com/a/e08caf6a0ac1e6b9



是否可以推导出函数指针类型?如果是这样,为什么我的尝试不工作,什么是正确的方式这样做?



如果这是不可能的,一个好的答案会解释为什么。 p>

尝试



一个人可以看到哪个 foo ()通过检查 call<>()的定义,但该信息对于编译器不可用于过载解析度。仍然,信息都在那里,它只需要拉入功能模板签名。这可以用表达式SFINAE。



在伪代码中,我们想要这样:

  template< IgnoreThis,typename ReturnType> 
struct expr_check
{
typedef返回类型类型;
}

template< typename F>
expr_check<表达式要求F具有正确的签名,result_of< F> :: type> :: type
call(F f);

这是实际代码中的想法。



http://coliru.stacked-crooked.com/a/a3ce828d6cb16c2d



函数模板签名是:

  template< typename F& 
typename expr_check< sizeof(declat>()()),typename func_ptr_result< F> :: type> :: type
call(F f);

template< typename F,typename A>
typename expr_check< sizeof(declval< F>()(declat< A>())),typename func_ptr_result< F> :: type> :: type
call(F f,A a);

我目前没有编译。从编译器输出,你可以看到,在两个尝试实例化函数模板有一个调用<>()重载的替换失败,另一个简单的给出一个不透明的无法推导出模板参数。



(病毒编译为C ++ 03,但C ++ 11答案很好。)



我的怀疑是,虽然实例化 call<>() foo()没有被调用,并且C ++根本不提供 foo()在此上下文中的重载解析。没关系,可以证明一个 foo()重载是正确的,C ++只是不强制重载分辨率。另一方面,重载分辨率不限于被调用的函数。适当类型的函数指针选择 foo()的重载。



strong>



有几个问题询问关于函数指针类型的重载。看起来这是不可能做到的。我没有找到任何问题,试图通过表达SFINAE这样做。



这似乎是最接近的相关问题。



是否有方法可以推导出函数指针模板参数的值?



奖金计划



是函数指针在标题中使用的正确短语吗?

解决方案

如前所述,SFINAE不工作,因为重载函数在C ++中没有确定的类型,因此模板参数替换在这个阶段甚至不会发生。



但是,在你的例子中,有太多的foo的重载,但是太少的call重载。只需使用typename F 两个模板提供期望一个函数指针的模板。编译器现在可以根据上下文做正确的事情:

  #include< iostream> 

//函数
int foo(){return 0; }

int foo(int){return 1; }

//函数对象
struct Foo
{
int operator()()const {return 2; }
int operator()(int)const {return 3; }
};

//高阶函数/模板
模板< 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<< '\\\
'; // 0,1,2,3
}

通过添加返回类型扣除。在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);


In this example a function is passed to an implicitly instantiated function template.

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

We can break this code by adding an overload for foo().

int foo(int i) { return 0; }

The name "foo" is now ambiguous and the example will no longer compile. This can be made to compile by explicitly providing function pointer type info.

int (*func_takes_void)() = foo;
int a = call(func_takes_void);

int (*func_takes_int)(int) = foo;
int b = call(func_takes_int, 0);

http://coliru.stacked-crooked.com/a/e08caf6a0ac1e6b9

Is it possible to instead deduce the function pointer types? If so, why does my attempt below not work and what is the right way to do this?

If this is not possible, a good answer would explain why.

Attempt thus far

A human can see which foo() is intended in the two calls by inspecting the definitions of call<>() but that info is not available to the compiler for overload resolution. Still, the information is all there, it just needs to be pulled into the function template signature. This may be possible with expression SFINAE.

In pseudo code we want this:

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

Here is that idea worked out in real code.

http://coliru.stacked-crooked.com/a/a3ce828d6cb16c2d

The function template signatures are:

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

What I currently have does not compile. From the compiler output you can see that on both attempts to instantiate the function template there is substitution failure on one call<>() overload and the other simply gives an opaque "couldn't deduce template parameter".

(The colirus were compiled as C++03 but C++11 answers are fine.)

My suspicion is that while instantiating call<>(), foo() is not being called and C++ simply does not provide for overload resolution of foo() in this context. It doesn't matter that it can be proven that one foo() overload is the correct one, C++ just doesn't mandate overload resolution here. On the other hand, overload resolution isn't limited to a function being called. A function pointer of appropriate type gets to select overloads of foo().

Related questions

There are a few questions asking about overloading on function pointer type. It looks like this can't be done. I didn't find any questions trying to do this through expression SFINAE.

This seems to be the closest related question.

Is there a way to deduce the value of a function pointer template parameter?

Bonus pedantry

Is "function pointer" the correct phrase to have used in the title? Would "function reference" have been more accurate?

解决方案

As has been mentioned before, SFINAE doesn't work because the names of overloaded functions have no definite type in C++, therefore template parameter substitution doesn't even happen at this stage.

However, in your example, the problem is arguably not that you have too many overloads of "foo", but too few overloads of "call". Just provide both the templates with typename F and the ones that expect a function pointer. The compiler will now be able to do the right thing depending on context:

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

The call overloads can be made more generic by adding return type deduction. In C++11, this is possible even with function objects by using decltype rsp. result_of. For brevity, I will post only the new function signatures, as the bodies don't need to be changed in this case:

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

这篇关于表达式SFINAE重载类型的传递函数指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆