Expression SFINAE to overload on type of passed function pointer

后端 未结 2 464
一生所求
一生所求 2021-01-18 06:47

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

// Function that will be passed as argument
int foo() { return 0; }

//         


        
相关标签:
2条回答
  • 2021-01-18 07:31

    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);
    
    0 讨论(0)
  • 2021-01-18 07:50

    The closest you can get is probably this:

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

    and pass sfoo (or sfoo()) instead of foo around.

    That is, create a function object type that encapsulates the entire overload set in the templatized operator().

    Then instead of overload resolution over a template argument, which does not exist, you get a template instantiation over the same argument, which is OK.

    0 讨论(0)
提交回复
热议问题