【问题标题】:How to pass the specific callback to a template function?如何将特定的回调传递给模板函数?
【发布时间】:2018-03-07 10:43:21
【问题描述】:

我有以下代码:

#include <iostream>
using namespace std;
template <class T>

int get_num(int k) {
    return k + 3;
}

float get_num(float k) {
    return k + 3;
}


template <class T1, class T2>
void function(T1 (*callback)(T2), T2 arg) {
    callback(arg);
}

int main() {
    // your code goes here
    function(get_num, 3);
    return 0;
}

我需要使用 int 参数调用 get_num() 函数。但是编译器得到这个错误:

prog.cpp: In function ‘int main()’: prog.cpp:21:21: error: no matching
function for call to ‘function(<unresolved overloaded function type>,
int)’   function(get_num, 3);
                 ^ prog.cpp:15:6: note: candidate: template<class T1, class T2> void function(T1 (*)(T2), T2)  void function(T1
(*callback)(T2), T2 arg) {
      ^~~~~~~~ prog.cpp:15:6: note:   template argument deduction/substitution failed: prog.cpp:21:21: note:   couldn't deduce
template parameter ‘T1’   function(get_num, 3);

怎么做?

【问题讨论】:

  • 这是template &lt;class T&gt; 故意的吗?
  • 你的意思是为什么要写 class 而不是 typename ?
  • 不,我的意思是如果int get_num(int k) 应该是一个函数或一个模板。它看起来有点像它的常规功能。
  • @ՎարդանԳրիգորյան 第 3 行(实际上是前 3 行)看起来不属于这里,是吗?

标签: c++ function c++11 templates callback


【解决方案1】:

从int get_num(int) 中删除template &lt;class T&gt; 以获得正常的重载集后,您可以使用Some programmer dude’s answer。

在这个答案中,我想详细说明如何仍然使用基于函数指针的参数。

如果您将参数切换为function 至少gcc is able to deduce it:

template <typename T, typename U>
void function2(T arg, U(*callback)(T)) {
    callback(arg);
}

clang 不喜欢你在那里使用U,所以如果你的返回类型总是和你的参数一样,you can use T twice:

template <typename T>
void function2(T arg, T(*callback)(T)) {
    callback(arg);
}

要解决一般错误消息中的歧义,您还可以使用do the overload resolution manually 和static_cast:

function(static_cast<float(*)(float)>(&get_num), 3.0f);
function(static_cast<int(*)(int)>(&get_num), 3);

【讨论】:

    【解决方案2】:

    一个问题是function 的返回类型和参数类型有不同的类型,但实际上两者是相同的。

    这意味着你可以做类似的事情

    template<typename T, typename F = T(T)>
    void function(F callback, T arg)
    {
        callback(arg);
    }
    

    模板参数F只是为了简化回调参数声明。

    【讨论】:

    • 也许,这不是错误。您需要使用 2 种不同的类型以防万一:std::string get_num (double a) { return "this is a string " + std::to_string(a); }
    • @SmitYcyken 这就是为什么F 是具有默认值的模板参数,而不是template&lt;typename T&gt; void function(T(*callback)(T), T arg)
    【解决方案3】:

    int get_num(int k) 前面有一个template &lt;class T&gt;。让我们假设它不存在,然后这个工作:

    有时您无法将函数更改为模板,但需要使用指向具有多个重载的函数的函数指针。选择正确的重载方法是指定函数指针的类型(因为不同的重载函数指针的类型不同)。

    typedef int (* int_get_num_t)(int);
    int main() {
        int_get_num_t correct_overload = get_num;
        function(correct_overload, 3);
        return 0;
    }
    

    如果int get_num(int k) 真的应该是一个模板(那么为什么浮动的不是?)那么你只需要选择模板版本:

    int_get_num_t correct_overload = get_num<int>;
    

    实际上,您可以传递任何类型而不是 int,因为您的模板 get_num 始终采用 int 并返回 int,而与模板参数无关。

    最后...您实际上不需要get_num 的第二个重载,但您只需要一个模板。在这种情况下,您仍然需要选择正确的模板来获取函数指针:

    template <typename T>    
    T get_num(T k) { return k + 3; }
    
    template <class T1, class T2>
    void function(T1 (*callback)(T2), T2 arg) {
        callback(arg);
    }
    
    int main() {
        int_get_num_t correct_overload = get_num<int>;
        function(correct_overload, 3);
        return 0;
    }
    

    【讨论】:

    • 无法编译,因为它无法为get_num 推导出T。
    • @nwp 我错过了template&lt;class T&gt;,我仍然不确定它是否是故意的。感谢您发现错误,我希望现在更好;)
    【解决方案4】:

    这是使用 C++ 仿函数的那个。

    #include <iostream>
    using namespace std;
    
    template<class T>
    struct get_num : public std::unary_function<T,T>
    {
      T operator()(const T& k) {
        return k+3;
      }
    };
    
    template< class T1, class T2 >
    void function( T1 fun, T2 arg)
    {
        fun(arg);
        cout << fun(arg) << endl;
    }
    
    int main()
    {
        function(get_num<int>(), 3);
        return 0;
    }
    

    【讨论】:

      【解决方案5】:

      以下代码将起作用:

      #include <iostream>
      
      using namespace std;
      
      template<typename T>
      int get_num(int k) {
          return k + 3;
      }
      
      float get_num(float k) {
          return k + 3;
      }
      
      
      template<typename T1, typename T2> // Maybe here you want the `typename`, not the `class`
      void f(T1 (*callback)(T2), T2 arg) {
          callback(arg);
      }
      
      int main() {
          // your code goes here
          f(get_num<int>, 3); // The key point is here!
          return 0;
      }
      

      你得到编译错误的原因是如果你只使用get_num,编译器无法推断出类型T,因为所有参数都与类型T无关。

      【讨论】:

      • typename 和 class 在声明模板类型参数时完全可以互换
      【解决方案6】:

      你必须指定函数的类型

      #include <iostream> 
      #include <string>
      int get_num(int k) {
          return k + 3;
      }
      
      float get_num(float k) {
          return k + 3;
      }
      
      std::string get_num (double a)
      {
          return "this is a string " + std::to_string(a);
      }
      
      template <class T1, class T2>
      using callback = T1(*)(T2);
      
      
      template <class T1, class T2>
      void function(callback<T1, T2> function, T2 arg) {
          std:: cout << function(arg) << std::endl;
      }
      
      int main() {
          // your code goes here
          function<int, int>(get_num, 3);
      
          function<std::string, double>(get_num, 3);
      
          system("pause");
          return 0;
      }
      

      为什么有 2 个不同的模板参数? -OP的问题不是关于优化,而是关于

      如何将特定的回调传递给模板函数?

      所以,这是解决特定错误的众多实现之一。

      【讨论】:

      • 吹毛求疵,但将参数命名为与其所在的函数相同,这有点令人困惑。
      【解决方案7】:

      我允许自己简化一点你的代码。这应该可以正常工作:

      #include <iostream>
      using namespace std;
      
      template <class T>
      T get_num(T k) {
          return k + 3;
      }
      
      template <class T1, class T2>
      void function(T1 callback, T2 arg) {
          callback(arg);
      }
      
      int main() {
          function(get_num<int>, 3);
          return 0;
      }
      

      【讨论】:

        【解决方案8】:

        我想提供一个稍微不同的解决方案。 我在代码中对其进行了解释,希望它更易于阅读和理解:

        // create a helper class, 
        // which collects all callable classes to build one callable object later
        template<class... Ts> struct funcs : Ts... { using Ts::operator()...; };
        template<class... Ts> funcs(Ts...) -> funcs<Ts...>;
        
        // instead of free functions, build objects with methods
        // and use operator() instead of function names.
        // this makes it easier to "understand" that this will be an callable object
        struct Func1
        {
            int operator()(int k) {
                return k + 3;
            }
        };
        
        struct Func2
        {
            float operator()(float k) {
                return k + 3;
            }
        };
        
        // adapt your code to this:
        template <class T1, class T2>
        auto function(T1 callback, T2 arg) {
            return callback(arg);
        }
        
        // and finaly you can use it this way, also with return types 
        // the central hack is:
        // funcs{ Func1(), Func2() }
        // this will generate a callable object with all the overloads
        // from the inserted callable objects
        int main() {
            // your code goes here
            std::cout << function(funcs{ Func1(), Func2() }, 3) << std::endl;
            std::cout << function(funcs{ Func1(), Func2() }, (float)7.999) << std::endl;
            return 0;
        }
        

        【讨论】:

        • 建议:分解代码并逐段解释它的作用。评论不是那么容易阅读
        猜你喜欢
        • 1970-01-01
        • 2021-11-10
        • 2011-11-15
        • 2014-03-11
        • 1970-01-01
        • 2013-06-06
        • 2017-12-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多