简体   繁体   English

模板std :: function作为类成员函数C ++

[英]Template std::function as class member function C++

I would like to store a std::function in a class as a member. 我想将std :: function存储在类中作为成员。
I have troubles with below test code when calling a.callMethod() where the method has been set just before with a.setMethod() . 我在调用a.callMethod()时遇到了以下测试代码的麻烦,其中方法刚刚使用a.setMethod()设置 The code works fine if I remove the template. 如果我删除模板,代码工作正常。
I have tried to debug with a function callMethodOutsideClass without success. 我试图使用函数callMethodOutsideClass进行调试但没有成功。
Is there a better way to manage that ? 有没有更好的方法来管理它?

#include <iostream>
#include <vector>
#include <functional>

template<typename T>
struct A
{
    A(size_t size, T value) : vec_(size, value), method_(nullptr) {}
    void setMethod(const std::function<int(A<T>&)> method)  { method_ = method; }
    int callMethod()
    {
        if(method_)
            return method_(*this);
        else
            std::cerr << "method not initialized" << std::endl;
        return -1;
    }

    std::vector<int> vec_;
    std::function<int(A<T>& a)> method_;
};

template<typename T>
int callMethodOutsideClass(struct A<T>& a, const std::function<int(A<T>&)> method)
{
    return method(a);
}

template<typename T>
int apple(struct A<T>& a)
{
    a.vec_[0] += 1;
    return 1;
}

template<typename T>
int orange(struct A<T>& a)
{
    a.vec_[0] += 2;
    return 2;
}

int main()
{
    A<int> a(10,4), b(10,4);
    std::cout << callMethodOutsideClass(a, &apple) << std::endl;

    a.setMethod(&orange);
    std::cout << a.callMethod() << std::endl;

    std::cout << a.vec_[0] << std::endl;
}

I currently get the following errors : 我目前收到以下错误:

Foo6.cpp: In function ‘int main()’:
Foo6.cpp:46:47: error: cannot resolve overloaded function ‘apple’ based on conversion to type ‘std::function<int(A<int>&)>’
  std::cout << callMethodOutsideClass(a, &apple) << std::endl;
                                               ^
Foo6.cpp:48:21: error: no matching function for call to ‘A<int>::setMethod(<unresolved overloaded function type>)’
  a.setMethod(&orange);
                     ^
Foo6.cpp:48:21: note: candidate is:
Foo6.cpp:9:7: note: void A<T>::setMethod(std::function<int(A<T>&)>) [with T = int]
  void setMethod(const std::function<int(A<T>&)> method)  { method_ = method; }
       ^
Foo6.cpp:9:7: note:   no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘std::function<int(A<int>&)>’

A pointer to function is not a std::function<T> . 指向函数的指针不是 std::function<T> The std::function<T> signature can't be deduced based on the function address given as an argument. 无法根据作为参数给出的函数地址推导出std::function<T>签名。 In addition, the compiler can't resolve a proper function template specialization to get its address when a conversion to std::function<T> is requested, since the constructor of std::function<T> is a function template as well. 此外,当请求转换为std::function<T>时,编译器无法解析正确的函数模板特化以获取其地址,因为std::function<T>的构造std::function<T>也是函数模板。

You need to be more explicit: 你需要更明确:

std::cout << callMethodOutsideClass<int>(a, &apple<int>) << std::endl;
//                                 ^^^^^          ^^^^^
a.setMethod(&orange<int>);
//                 ^^^^^

Is there any way to deduce templates parameters "easily" ? 有没有办法“轻松”推断出模板参数?

You can modify the signature of callMethodOutsideClass in one of two ways: 您可以通过以下两种方式之一修改callMethodOutsideClass的签名:

Option #1: 选项1:

Disable a type deduction on a std::function<int(A<T>&)> parameter: 禁用std::function<int(A<T>&)>参数的类型推导:

template <typename T> struct identity { using type = T; };

template<typename T>
int callMethodOutsideClass(A<T>& a, const typename identity<std::function<int(A<T>&)>>::type method)
{
    return method(a);
}

But you'll have to pay for the type-erasure applied by a std::function . 但是你必须为std::function应用的类型擦除付费。

Option #2: 选项#2:

Let the compiler deduce the real type of a functor object given as an argument: 让编译器推导出作为参数给出的仿函数对象的实际类型:

template <typename T, typename F>
int callMethodOutsideClass(A<T>& a, F&& method)
{
    return std::forward<F>(method)(a);
}

In both cases you can just say: 在这两种情况下你都可以说:

callMethodOutsideClass(a, &apple<int>);
//                              ^^^^^

Note: You still have to pass the address of a concrete function template specialization by providing a list of template arguments &apple<int> . 注意:您仍然必须通过提供模板参数列表&apple<int>来传递具体函数模板特化的地址。 If you want to get away with a simple &address syntax, then the function taking it needs to declare an exact type of that argument: 如果你想要使用简单的&address语法,那么获取它的函数需要声明该参数的确切类型:

template<typename T>
int callMethodOutsideClass(A<T>& a, int(*method)(A<T>&))
{
    return method(a);
}

callMethodOutsideClass(a, &apple);

or you could help the compiler resolve the proper overload at a call site: 或者您可以帮助编译器解决呼叫站点上的正确过载:

callMethodOutsideClass(a, static_cast<int(*)(decltype(a)&)>(&apple));

...or, you can use a lambda expression defined as follows: ...或者,您可以使用如下定义的lambda表达式:

template<typename T, typename F>
int callMethodOutsideClass(struct A<T>& a, F&& method)
{
    return std::forward<F>(method)(a);
}

// in C++11:
callMethodOutsideClass(a, [](decltype(a)& x){return apple(x);});
// in C++14:
callMethodOutsideClass(a, [](auto&& x){return apple(std::forward<decltype(x)>(x));});

As far as the setMethod member function is concerned, the things are easier, since the compiler knows exactly that it expects const std::function<int(A<T>&)> method where T is known (not deduced). setMethod成员函数而言,事情更容易,因为编译器确切地知道它需要const std::function<int(A<T>&)> method ,其中T是已知的(未推断)。 So basically, you just need to help the compiler to get the address of a function template specialzation you need at the call site: 基本上,您只需要帮助编译器获取调用站点所需的函数模板特化的地址:

a.setMethod(&orange<int>);
a.setMethod(static_cast<int(*)(decltype(a)&)>(&orange));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM