【问题标题】:Expression SFINAE to overload on type of passed function pointer表达式 SFINAE 重载传递的函数指针的类型
【发布时间】:2014-12-17 10:42:07
【问题描述】:

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

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

我们可以通过为foo() 添加重载来打破这段代码。

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

是否可以改为推断函数指针类型?如果是这样,为什么我在下面的尝试不起作用,正确的方法是什么?

如果这不可能,一个好的答案会解释原因。

目前的尝试

人们可以通过检查call&lt;&gt;() 的定义来查看两个调用中的哪个foo(),但编译器无法获得该信息以进行重载解析。尽管如此,信息都在那里,只需将其拉入函数模板签名中。这可以通过表达式 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);

这是用实际代码实现的想法。

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

我目前拥有的内容无法编译。从编译器输出中您可以看到,在两次实例化函数模板的尝试中,一个 call&lt;&gt;() 重载的替换失败,而另一个只是给出一个不透明的“无法推断模板参数”。

(大肠杆菌被编译为 C++03,但 C++11 的答案很好。)

我的怀疑是,在实例化 call&lt;&gt;() 时,foo() 没有被调用,并且 C++ 在这种情况下根本不提供 foo() 的重载解决方案。可以证明一个foo() 重载是正确的并不重要,C++ 只是在这里不强制要求重载解决方案。另一方面,重载决议不限于被调用的函数。适当类型的函数指针可以选择 foo() 的重载。

相关问题

有几个关于函数指针类型重载的问题。看来这是无法做到的。我没有发现任何问题试图通过表达式 SFINAE 做到这一点。

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

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

奖金学究

“函数指针”是标题中使用的正确短语吗? “函数引用”会更准确吗?

【问题讨论】:

  • 这是一个错误的尝试方向。您假装可以将函数 name 作为模板参数传递,并根据 name 和参数类型在内部执行重载解析。你不能。模板参数是实体,而不是名称。
  • 这是有道理的。是否可以以任何方式推断传递的函数类型?或者,嗯,推断出它们必须是什么才能编译?嗯,我想措辞强调了问题的模糊性。
  • 你可以用R (*)()R (*)(A)替换F(在call声明的参数列表中),用typename R替换typename F(我选择了R,因为它是返回类型),尽管您需要多个变体来实现 A 上 cv-ref 限定符的不同组合。
  • 您实际上想从中实现什么?你能举个例子吗?我认为传递一个调用你的 lambda 会更合适。

标签: c++ templates c++11 function-pointers sfinae


【解决方案1】:

你能得到的最接近的可能是这样的:

struct sfoo
{
  template<typename... args>
  void operator() (args&&... a)
  { 
    foo(std::forward<args>(a)...);
  }
};

并传递sfoo(或sfoo())而不是foo

即创建一个函数对象类型,将整个重载集封装在模板化的operator()中。

然后,不是对不存在的模板参数进行重载解析,而是在同一参数上获得模板实例化,这没关系。

【讨论】:

    【解决方案2】:

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

    但是,在您的示例中,问题可能不在于您的“foo”重载过多,而是“call”重载过少。只需提供 both 类型名称为 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
    }
    

    可以通过添加返回类型推导使调用重载更加通用。在 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);
    

    【讨论】:

    • std::forward&lt;args&gt;(a)...) 评论也适用于此。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多