C++模板函数从可调用类实例到std::function

huangapple go评论85阅读模式
英文:

C++ template function from callable class instance to std::function

问题

I do need std::function for stateful operation.

So I would like to make an instance of a class implementing operator() into std::function. I did test std::bind works, but can't wrap it up as a function.

struct StatefulOperation
{
    StatefulOperation(int num) : num_(num) {}
    int operator()(int i)
    {
        num_ += i;
        return num_;
    }
    
    int num_;
};


// This works

const StatefulOperation obj(40);
    
using std::placeholders::_1;
std::function<int(int)> op = std::bind(&StatefulOperation::operator(), obj, _1);


// Want something like this

template<typename A, typename B, typename C>
std::function<B(A)> op(C& obj);


// This doesn't work

template<typename A, typename B, typename C>
std::function<B(A)> op(C& obj)
{
    using std::placeholders::_1;
    return std::bind(&C::operator(), obj, _1);
}

The last implementation attempt fails with an error like below:

main.cpp:52:21: note:   template argument deduction/substitution failed:
main.cpp:75:35: note:   couldn't deduce template parameter 'A'
   75 |     std::function<int(int)> f = op(a);
      |                                 ~~^~~

How can I get this done?

英文:

I do need std::function for stateful operation.

So I would like to make an instance of a class implementing operator() into std::function. I did test std::bind works, but can't wrap it up as a function.

struct StatefulOperation
{
    StatefulOperation(int num) : num_(num) {}
    int operator()(int i)
    {
        num_ += i;
        return num_;
    }
    
    int num_;
};


// This works

const StatefulOperation obj(40);
    
using std::placeholders::_1;
std::function&lt;int(int)&gt; op = std::bind(&amp;StatefulOperation::operator(), obj, _1);


// Want something like this

template&lt;typename A, typename B, typename C&gt;
std::function&lt;B(A)&gt; op(C&amp; obj);


// This doesn&#39;t work

template&lt;typename A, typename B, typename C&gt;
std::function&lt;B(A)&gt; op(C&amp; obj)
{
    using std::placeholders::_1;
    return std::bind(&amp;C::operator(), obj, _1);
}

The last implementation attempt fails with error like below;

main.cpp:52:21: note:   template argument deduction/substitution failed:
main.cpp:75:35: note:   couldn’t deduce template parameter ‘A’
   75 |     std::function&lt;int(int)&gt; f = op(a);
      |                                 ~~^~~

How can I get this done?

答案1

得分: 2

编译器无法仅从返回类型中推断出AB,您需要指定它们:

std::function<int(int)> f = op<int, int>(a);

然而,您实际上不需要这些额外的模板参数。只需使用auto来推断返回类型:

template<typename C>
auto op(C& obj)
{
    using std::placeholders::_1;
    return std::bind(&C::operator(), std::ref(obj), _1);
}

返回类型不会完全相同,但可以转换为您想要的std::function<B(A)>。如果您想指定确切的返回类型,可以结合使用thisstd::invoke_result_t来实现。

英文:

The compiler cannot deduce A and B just from the return type. You need to specify them:

std::function&lt;int(int)&gt; f = op&lt;int, int&gt;(a);

However, you don't really need these additional template parameters. Just use auto to deduce the return type:

template&lt;typename C&gt;
auto op(C&amp; obj)
{
    using std::placeholders::_1;
    return std::bind(&amp;C::operator(), std::ref(obj), _1);
}

The return type will not be exactly the same, but convertible to what you want (std::function&lt;B(A)&gt;). If you want to specify the exact return type, you can do so with a combination of this and std::invoke_result_t.

答案2

得分: 1

Instead of using bind you should use lambda expressions. Also you StatefulOperation should NOT be const (otherwise it can't update its internal state).

#include <functional>
#include <iostream>

struct StatefulOperation
{
    StatefulOperation(int num) : num_(num) {}
    int operator()(int i)
    {
        num_ += i;
        return num_;
    }

    int num_;
};

int main()
{
    /*const*/ StatefulOperation obj(40); // your state cannot be const since you are changing its internal state

    // instead of bind rely on lambda expressions (bind is still there because of well... history)
    std::function<int(int)> op{ [&](int num) { return obj(num); } };

    for (int i = 0; i < 10; ++i)
    {
        std::cout << op(i) << " ";
    }

    return 0;
}
英文:

Instead of using bind you should use lambda expressions. Also you StatefulOperation should NOT be const (otherwise it can't update its internal state).

#include &lt;functional&gt;
#include &lt;iostream&gt;

struct StatefulOperation
{
    StatefulOperation(int num) : num_(num) {}
    int operator()(int i)
    {
        num_ += i;
        return num_;
    }

    int num_;
};

int main()
{
    /*const*/ StatefulOperation obj(40); // your state cannot be const since you are changing its internal state
    
    // instead of bind rely on lambda expressions (bind is still there because of well... history)
    std::function&lt;int(int)&gt; op{ [&amp;](int num) { return obj(num); } };

    for (int i = 0; i &lt; 10; ++i)
    {
        std::cout &lt;&lt; op(i) &lt;&lt; &quot; &quot;;
    }

    return 0;
}

答案3

得分: 0

你可以用最简单的方式初始化一个std::function,因为你的类是可调用的,实现了一个具有期望类型的函数调用运算符:

std::function<int(int)> op{StatefulOperation(40)};

不要忘记,当复制op时,状态也会被复制。

英文:

Since your class is a callable that implements a function call operator with the expected types, you can initialize a std::function in the most simple way:

std::function&lt;int(int)&gt; op{StatefulOperation(40)};

Do not forget that the state is copied when op is copied.

huangapple
  • 本文由 发表于 2023年7月4日 20:43:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76612778.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定