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

时间:2018-03-07 10:43:22

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

我有以下代码:

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

怎么做?

8 个答案:

答案 0 :(得分:2)

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

这意味着您可以执行类似

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

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

答案 1 :(得分:2)

template <class T>前面有int get_num(int k)。让我们假设它不存在,然后这可行:

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

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

答案 2 :(得分:2)

这是使用 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;
}

答案 3 :(得分:2)

template <class T>移除int get_num(int)以获得正常的过载设置后,您可以使用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);
}
当你在那里使用U时,

clang不喜欢它,所以如果你的返回类型总是与你的参数相同,you can use T twice

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

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

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

答案 4 :(得分:1)

您必须指定功能类型

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

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

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

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

答案 5 :(得分:1)

以下代码可以使用:

Private Sub datagridview1_SelectionChanged(sender As Object, e As EventArgs) Handles datagridview1.SelectionChanged
        datagridview1.ClearSelection()
End Sub

您收到编译错误的原因是,如果您只使用#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; } ,则编译器无法推断出类型T,因为所有参数都不属于get_num类型。

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

我想提供一个稍有不同的解决方案。 我在代码中解释它,使它更有助于阅读和理解:

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